From ac497466958adce552ffff33985e12253a7f4ee3 Mon Sep 17 00:00:00 2001 From: Kenvin Davies Date: Thu, 8 Mar 2018 20:16:01 +0100 Subject: [PATCH] doc: First step of error and async doc PR-URL: https://github.com/nodejs/node-addon-api/pull/272 Reviewed-By: Kyle Farnung Reviewed-By: Michael Dawson --- README.md | 2 + doc/async_operations.md | 24 +++- doc/async_worker.md | 309 +++++++++++++++++++++++++++++++++++++++- doc/error.md | 116 ++++++++++++++- doc/error_handling.md | 157 +++++++++++++++++++- doc/range_error.md | 59 ++++++++ doc/setup.md | 2 +- doc/type_error.md | 59 ++++++++ 8 files changed, 714 insertions(+), 14 deletions(-) create mode 100644 doc/range_error.md create mode 100644 doc/type_error.md diff --git a/README.md b/README.md index e4bfd25..c4fba45 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ values. Concepts and operations generally map to ideas specified in the - [PropertyDescriptor](doc/property_descriptor.md) - [Error Handling](doc/error_handling.md) - [Error](doc/error.md) + - [TypeError](doc/type_error.md) + - [RangeError](doc/range_error.md) - [Object Lifetime Management](doc/object_lifetime_management.md) - [HandleScope](doc/handle_scope.md) - [EscapableHandleScope](doc/escapable_handle_scope.md) diff --git a/doc/async_operations.md b/doc/async_operations.md index 3993468..b8dec37 100644 --- a/doc/async_operations.md +++ b/doc/async_operations.md @@ -1,5 +1,23 @@ # Asynchronous operations -You are reading a draft of the next documentation and it's in continuous update so -if you don't find what you need please refer to: -[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) +Node.js native add-ons often need to execute long running tasks and to avoid +blocking the **event loop** they have to run them asynchronously from the +**event loop**. +In the Node.js model of execution the event loop thread represents the thread +where JavaScript code is executing. The node.js guidance is to avoid blocking +other work queued on the event loop thread. Therefore, we need to do this work on +another thread. + +All this means that native add-ons need to leverage async helpers from libuv as +part of their implementation. This allows them to schedule work to be executed +asynchronously so that their methods can return in advance of the work being +completed. + +Node Addon API provides an interface to support functions that cover +the most common asynchronous use cases. There is an abstract classes to implement +asynchronous operations: + +- **[AsyncWorker](async_worker.md)** + +These class helps manage asynchronous operations through an abstraction +of the concept of moving data between the **event loop** and **worker threads**. diff --git a/doc/async_worker.md b/doc/async_worker.md index a1a9689..0d78c1b 100644 --- a/doc/async_worker.md +++ b/doc/async_worker.md @@ -1,5 +1,306 @@ -# Async worker +# AsyncWorker -You are reading a draft of the next documentation and it's in continuous update so -if you don't find what you need please refer to: -[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) +`AsyncWorker` is an abstract class that you can subclass to remove many of the +tedious tasks of moving data between the event loop and worker threads. This +class internally handles all the details of creating and executing an asynchronous +operation. + +Once created, execution is requested by calling `Queue`. When a thread is +available for execution the `Execute` method will be invoked. Once `Execute` +complets either `OnOK` or `OnError` will be invoked. Once the `OnOK` or +`OnError` methods are complete the AsyncWorker instance is destructed. + +For the most basic use, only the `Execute` method must be implemented in a +subclass. + +## Methods + +### Env + +Requests the environment in which the async worker has been initially created. + +```cpp +Env Env() const; +``` + +Returns the environment in which the async worker has been created. + +### Queue + +Requests that the work be queued for execution. + +```cpp +void Queue(); +``` + +### Cancel + +Cancels queued work if it has not yet been started. If it has already started +executing, it cannot be cancelled. If cancelled successfully neither +`OnOK` nor `OnError` will be called. + +```cpp +void Cancel(); +``` + +### Receiver + +```cpp +ObjectReference& Receiver(); +``` + +Returns the persistent object reference of the receiver object set when the async +worker was created. + +### Callback + +```cpp +FunctionReference& Callback(); +``` + +Returns the persistent function reference of the callback set when the async +worker was created. The returned function reference will receive the results of +the computation that happened in the `Execute` method, unless the default +implementation of `OnOK` or `OnError` is overridden. + +### SetError + +Sets the error message for the error that happened during the execution. Setting +an error message will cause the `OnError` method to be invoked instead of `OnOK` +once the `Execute` method completes. + +```cpp +void SetError(const std::string& error); +``` + +- `[in] error`: The reference to the string that represent the message of the error. + +### Execute + +This method is used to execute some tasks out of the **event loop** on a libuv +worker thread. Subclasses must implement this method and the method is run on +a thread other than that running the main event loop. As the method is not +running on the main event loop, it must avoid calling any methods from node-addon-api +or running any code that might invoke JavaScript. Instead once this method is +complete any interaction through node-addon-api with JavaScript should be implemented +in the `OnOK` method which runs on the main thread and is invoked when the `Execute` +method completes. + +```cpp +virtual void Execute() = 0; +``` + +### OnOK + +This method is invoked when the computation in the `Excecute` method ends. +The default implementation runs the Callback provided when the AsyncWorker class +was created. + +```cpp +virtual void OnOK(); +``` + +### OnError + +This method is invoked afer Execute() completes if an error occurs +while `Execute` is running and C++ exceptions are enabled or if an +error was set through a call to `SetError`. The default implementation +calls the callback provided when the AsyncWorker class was created, passing +in the error as the first parameter. + +```cpp +virtual void OnError(const Error& e); +``` + +### Constructor + +Creates a new `AsyncWorker`. + +```cpp +explicit AsyncWorker(const Function& callback); +``` + +- `[in] callback`: The function which will be called when an asynchronous +operations ends. The given function is called from the main event loop thread. + +Returns an AsyncWork instance which can later be queued for execution by calling +`Queue`. + +### Constructor + +Creates a new `AsyncWorker`. + +```cpp +explicit AsyncWorker(const Function& callback, const char* resource_name); +``` + +- `[in] callback`: The function which will be called when an asynchronous +operations ends. The given function is called from the main event loop thread. +- `[in] resource_name`: Null-terminated strings that represents the +identifier for the kind of resource that is being provided for diagnostic +information exposed by the async_hooks API. + +Returns an AsyncWork instance which can later be queued for execution by calling +`Queue`. + + +### Constructor + +Creates a new `AsyncWorker`. + +```cpp +explicit AsyncWorker(const Function& callback, const char* resource_name, const Object& resource); +``` + +- `[in] callback`: The function which will be called when an asynchronous +operations ends. The given function is called from the main event loop thread. +- `[in] resource_name`: Null-terminated strings that represents the +identifier for the kind of resource that is being provided for diagnostic +information exposed by the async_hooks API. +- `[in] resource`: Object associated with the asynchronous operation that +will be passed to possible async_hooks. + +Returns an AsyncWork instance which can later be queued for execution by calling +`Queue`. + +### Constructor + +Creates a new `AsyncWorker`. + +```cpp +explicit AsyncWorker(const Object& receiver, const Function& callback); +``` + +- `[in] receiver`: The `this` object passed to the called function. +- `[in] callback`: The function which will be called when an asynchronous +operations ends. The given function is called from the main event loop thread. + +Returns an AsyncWork instance which can later be queued for execution by calling +`Queue`. + + +### Constructor + +Creates a new `AsyncWorker`. + +```cpp +explicit AsyncWorker(const Object& receiver, const Function& callback,const char* resource_name); +``` + +- `[in] receiver`: The `this` object passed to the called function. +- `[in] callback`: The function which will be called when an asynchronous +operations ends. The given function is called from the main event loop thread. +- `[in] resource_name`: Null-terminated strings that represents the +identifier for the kind of resource that is being provided for diagnostic +information exposed by the async_hooks API. + +Returns an AsyncWork instance which can later be queued for execution by calling +`Queue`. + + +### Constructor + +Creates a new `AsyncWorker`. + +```cpp +explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); +``` + +- `[in] receiver`: The `this` object passed to the called function. +- `[in] callback`: The function which will be called when an asynchronous +operations ends. The given function is called from the main event loop thread. +- `[in] resource_name`: Null-terminated strings that represents the +identifier for the kind of resource that is being provided for diagnostic +information exposed by the async_hooks API. +- `[in] resource`: Object associated with the asynchronous operation that +will be passed to possible async_hooks. + +Returns an AsyncWork instance which can later be queued for execution by calling +`Queue`. + +### Destructor + +Deletes the created work object that is used to execute logic asynchronously. + +```cpp +virtual ~AsyncWorker(); +``` + +## Operator + +```cpp +operator napi_async_work() const; +``` + +Returns the N-API napi_async_work wrapped by the AsyncWorker object. This can be +used to mix usage of the C N-API and node-addon-api. + +## Example + +The first step to use the `AsyncWorker` class is to create a new class that inherit +from it and implement the `Execute` abstract method. Typically input to your +worker will be saved within class' fields generally passed in through its +constructor. + +When the `Execute` method completes without errors the `OnOK` function callback +will be invoked. In this function the results of the computation will be +reassembled and returned back to the initial JavaScript context. + +`AsyncWorker` ensures that all the code in the `Execute` function runs in the +background out of the **event loop** thread and at the end the `OnOK` or `OnError` +function will be called and are executed as part of the event loop. + +The code below show a basic example of `AsyncWorker` the implementation: + +```cpp +#include + +#include +#include + +use namespace Napi; + +class EchoWorker : public AsyncWorker { + public: + EchoWorker(Function& callback, std::string& echo) + : AsyncWorker(callback), echo(echo) {} + + ~EchoWorker() {} + // This code will be executed on the worker thread + void Execute() { + // Need to simulate cpu heavy task + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + void OnOK() { + HandleScope scope(Env()); + Callback().Call({Env().Null(), String::New(Env(), echo)}); + } + + private: + std::string echo; +}; +``` + +The `EchoWorker`'s contructor calls the base class' constructor to pass in the +callback that the `AsyncWorker` base class will store persistently. When the work +on the `Execute` method is done the `OnOk` method is called and the results return +back to JavaScript invoking the stored callback with its associated environment. + +The following code shows an example on how to create and and use an `AsyncWorker` + +```cpp +Value Echo(const CallbackInfo& info) { + // You need to check the input data here + Function cb = info[1].As(); + std::string in = info[0].As(); + EchoWorker* wk = new EchoWorker(cb, in); + wk->Queue(); + return info.Env().Undefined(); +``` + +Using the implementation of an `AsyncWorker` is straight forward. You need only create +a new instance and pass to its constructor the callback you want to execute when +your asynchronous task ends and other data you need for your computation. Once created the +only other action you have to do is to call the `Queue` method that will that will +queue the created worker for execution. diff --git a/doc/error.md b/doc/error.md index 6ffa884..dc5e7ea 100644 --- a/doc/error.md +++ b/doc/error.md @@ -1,5 +1,115 @@ # Error -You are reading a draft of the next documentation and it's in continuous update so -if you don't find what you need please refer to: -[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) +The **Error** class is a representation of the JavaScript Error object that is thrown +when runtime errors occur. The Error object can also be used as a base object for +user-defined exceptions. + +The **Error** class is a persistent reference to a JavaScript error object thus +inherits its behavior from the `ObjectReference` class (for more info see: [ObjectReference](object_reference.md)). + +If C++ exceptions are enabled (for more info see: [Setup](setup.md)), then the +**Error** class extends `std::exception` and enables integrated +error-handling for C++ exceptions and JavaScript exceptions. + +For more details about error handling refer to the section titled [Error handling](error_handling.md). + +## Methods + +### New + +Creates empty instance of an `Error` object for the specified environment. + +```cpp +Error::New(Napi:Env env); +``` + +- `[in] Env`: The environment in which to construct the Error object. + +Returns an instance of `Error` object. + +### New + +Creates instance of an `Error` object. + +```cpp +Error::New(Napi:Env env, const char* message); +``` + +- `[in] Env`: The environment in which to construct the Error object. +- `[in] message`: Null-terminated string to be used as the message for the Error. + +Returns instance of an `Error` object. + +### New + +Creates instance of an `Error` object + +```cpp +Error::New(Napi:Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the `Error` object. +- `[in] message`: Reference string to be used as the message for the `Error`. + +Returns instance of an `Error` object. + +### Fatal + +In case of an unrecoverable error in a native module, a fatal error can be thrown +to immediately terminate the process. + +```cpp +static NAPI_NO_RETURN void Fatal(const char* location, const char* message); +``` + +The function call does not return, the process will be terminated. + +### Constructor + +Creates empty instance of an `Error`. + +```cpp +Error(); +``` + +Returns an instance of `Error` object. + +### Constructor + +Initializes an `Error` instance from an existing JavaScript error object. + +```cpp +Error(napi_env env, napi_value value); +``` + +- `[in] Env`: The environment in which to construct the Error object. +- `[in] value`: The `Error` reference to wrap. + +Returns instance of an `Error` object. + +### Message + +```cpp +std::string& Message() const NAPI_NOEXCEPT; +``` + +Returns the reference to the string that represent the message of the error. + +### ThrowAsJavaScriptException + +Throw the error as JavaScript exception. + +```cpp +void ThrowAsJavaScriptException() const; +``` + +Throws the error as a JavaScript exception. + +### what + +```cpp +const char* what() const NAPI_NOEXCEPT override; +``` + +Returns a pointer to a null-terminated string that is used to identify the +exception. This method can be used only if the exception mechanism is enabled. diff --git a/doc/error_handling.md b/doc/error_handling.md index c540408..d562815 100644 --- a/doc/error_handling.md +++ b/doc/error_handling.md @@ -1,5 +1,156 @@ # Error handling -You are reading a draft of the next documentation and it's in continuous update so -if you don't find what you need please refer to: -[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/) +Error handling represents one of the most important considerations when +implementing a Node.js native add-on. When an error occurs in your C++ code you +have to handle and dispatch it correctly. **node-addon-api** uses return values and +JavaScript exceptions for error handling. You can choose return values or +exception handling based on the mechanism that works best for your add-on. + +The **Error** is a persistent reference (for more info see: [Object reference](object_reference.md)) +to a JavaScript error object. Use of this class depends on whether C++ +exceptions are enabled at compile time. + +If C++ exceptions are enabled (for more info see: [Setup](setup.md)), then the +**Error** class extends `std::exception` and enables integrated +error-handling for C++ exceptions and JavaScript exceptions. + +The following sections explain the approach for each case: + +- [Handling Errors With C++ Exceptions](#exceptions) +- [Handling Errors Without C++ Exceptions](#noexceptions) + + + +In most cases when an error occurs, the addon should do whatever clean is possible +and then return to JavaScript so that they error can be propagated. In less frequent +cases the addon may be able to recover from the error, clear the error and then +continue. + +## Handling Errors With C++ Exceptions + +When C++ exceptions are enabled try/catch can be used to catch exceptions thrown +from calls to JavaScript and then they can either be handled or rethrown before +returning from a native method. + +If a node-addon-api call fails without executing any JavaScript code (for example due to +an invalid argument), then node-addon-api automatically converts and throws +the error as a C++ exception of type **Error**. + +If a JavaScript function called by C++ code via node-addon-api throws a JavaScript +exception, then node-addon-api automatically converts and throws it as a C++ +exception of type **Error** on return from the JavaScript code to the native +method. + +If a C++ exception of type **Error** escapes from a N-API C++ callback, then +the N-API wrapper automatically converts and throws it as a JavaScript exception. + +On return from a native method, node-addon-api will automatically convert a pending C++ +exception to a JavaScript exception. + +When C++ exceptions are enabled try/catch can be used to catch exceptions thrown +from calls to JavaScript and then they can either be handled or rethrown before +returning from a native method. + +## Examples with C++ exceptions enabled + +### Throwing a C++ exception + +```cpp +Env env = ... +throw Error::New(env, "Example exception"); +// other C++ statements +// ... +``` + +The statements following the throw statement will not be executed. The exception +will bubble up as a C++ exception of type **Error**, until it is either caught +while still in C++, or else automatically propagated as a JavaScript exception +when returning to JavaScript. + +### Propagating a N-API C++ exception + +```cpp +Function jsFunctionThatThrows = someObj.As(); +Value result = jsFunctionThatThrows({ arg1, arg2 }); +// other C++ statements +// ... +``` + +The C++ statements following the call to the JavaScript function will not be +executed. The exception will bubble up as a C++ exception of type **Error**, +until it is either caught while still in C++, or else automatically propagated as +a JavaScript exception when returning to JavaScript. + +### Handling a N-API C++ exception + +```cpp +Function jsFunctionThatThrows = someObj.As(); +Value result; +try { + result = jsFunctionThatThrows({ arg1, arg2 }); +} catch (const Error& e) { + cerr << "Caught JavaScript exception: " + e.what(); +} +``` + +Since the exception was caught here, it will not be propagated as a JavaScript +exception. + + + +## Handling Errors Without C++ Exceptions + +If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the +**Error** class does not extend `std::exception`. This means that any calls to +node-addon-api function do not throw a C++ exceptions. Instead, it raises +_pending_ JavaScript exceptions and returns an _empty_ **Value**. +The calling code should check `env.IsExceptionPending()` before attempting to use a +returned value, and may use methods on the **Env** class +to check for, get, and clear a pending JavaScript exception (for more info see: [Env](env.md)). +If the pending exception is not cleared, it will be thrown when the native code +returns to JavaScript. + +## Examples with C++ exceptions disabled + +### Throwing a JS exception + +```cpp +Env env = ... +Error::New(env, "Example exception").ThrowAsJavaScriptException(); +return; +``` + +After throwing a JavaScript exception, the code should generally return +immediately from the native callback, after performing any necessary cleanup. + +### Propagating a N-API JS exception + +```cpp +Env env = ... +Function jsFunctionThatThrows = someObj.As(); +Value result = jsFunctionThatThrows({ arg1, arg2 }); +if (env.IsExceptionPending()) { + Error e = env.GetAndClearPendingException(); + return e.Value(); +} +``` + +If env.IsExceptionPending() is returns true a +JavaScript exception is pending. To let the exception propagate, the code should +generally return immediately from the native callback, after performing any +necessary cleanup. + +### Handling a N-API JS exception + +```cpp +Env env = ... +Function jsFunctionThatThrows = someObj.As(); +Value result = jsFunctionThatThrows({ arg1, arg2 }); +if (env.IsExceptionPending()) { + Error e = env.GetAndClearPendingException(); + cerr << "Caught JavaScript exception: " + e.Message(); +} +``` + +Since the exception was cleared here, it will not be propagated as a JavaScript +exception after the native callback returns. diff --git a/doc/range_error.md b/doc/range_error.md new file mode 100644 index 0000000..e0bd14d --- /dev/null +++ b/doc/range_error.md @@ -0,0 +1,59 @@ +# RangeError + +The **RangeError** class is a representation of the JavaScript RangeError that is +thrown when trying to pass a value as an argument to a function that does not allow +a range that includes the value. + +The **RangeError** class inherits its behaviors from the **Error** class (for +more info see: [Error](error.md)). + +For more details about error handling refer to the section titled [Error handling](error_handling.md). + +## Methods + +### New + +Creates a new instance of a `RangeError` object. + +```cpp +RangeError::New(Napi:Env env, const char* message); +``` + +- `[in] Env`: The environment in which to construct the `RangeError` object. +- `[in] message`: Null-terminated string to be used as the message for the `RangeError`. + +Returns an instance of a `RangeError` object. + +### New + +Creates a new instance of a `RangeError` object. + +```cpp +RangeError::New(Napi:Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the `RangeError` object. +- `[in] message`: Reference string to be used as the message for the `RangeError`. + +Returns an instance of a `RangeError` object. + +### Constructor + +Creates a new empty instance of a `RangeError`. + +```cpp +RangeError(); +``` + +### Constructor + +Initializes a `RangeError` instance from an existing Javascript error object. + +```cpp +RangeError(napi_env env, napi_value value); +``` + +- `[in] Env`: The environment in which to construct the `RangeError` object. +- `[in] value`: The `Error` reference to wrap. + +Returns an instance of a `RangeError` object. \ No newline at end of file diff --git a/doc/setup.md b/doc/setup.md index be95e74..176815a 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -19,7 +19,7 @@ To use **N-API** in a native module: ```json "dependencies": { - "node-addon-api": "1.1.0", + "node-addon-api": "1.2.0", } ``` diff --git a/doc/type_error.md b/doc/type_error.md new file mode 100644 index 0000000..7cfb9d1 --- /dev/null +++ b/doc/type_error.md @@ -0,0 +1,59 @@ +# TypeError + +The **TypeError** class is a representation of the JavaScript `TypeError` that is +thrown when an operand or argument passed to a function is incompatible with the +type expected by the operator or function. + +The **TypeError** class inherits its behaviors from the **Error** class (for more info +see: [Error](error.md)). + +For more details about error handling refer to the section titled [Error handling](error_handling.md). + +## Methods + +### New + +Creates a new instance of the `TypeError` object. + +```cpp +TypeError::New(Napi:Env env, const char* message); +``` + +- `[in] Env`: The environment in which to construct the `TypeError` object. +- `[in] message`: Null-terminated string to be used as the message for the `TypeError`. + +Returns an instance of a `TypeError` object. + +### New + +Creates a new instance of a `TypeError` object. + +```cpp +TypeError::New(Napi:Env env, const std::string& message); +``` + +- `[in] Env`: The environment in which to construct the `TypeError` object. +- `[in] message`: Reference string to be used as the message for the `TypeError`. + +Returns an instance of a `TypeError` object. + +### Constructor + +Creates a new empty instance of a `TypeError`. + +```cpp +TypeError(); +``` + +### Constructor + +Initializes a ```TypeError``` instance from an existing JavaScript error object. + +```cpp +TypeError(napi_env env, napi_value value); +``` + +- `[in] Env`: The environment in which to construct the `TypeError` object. +- `[in] value`: The `Error` reference to wrap. + +Returns an instance of a `TypeError` object. \ No newline at end of file