Skip to content

Commit 5a63f45

Browse files
NickNasomhdawson
authored andcommitted
doc: First step of error and async doc
PR-URL: #272 Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 9d38f61 commit 5a63f45

File tree

8 files changed

+714
-14
lines changed

8 files changed

+714
-14
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ values. Concepts and operations generally map to ideas specified in the
6464
- [PropertyDescriptor](doc/property_descriptor.md)
6565
- [Error Handling](doc/error_handling.md)
6666
- [Error](doc/error.md)
67+
- [TypeError](doc/type_error.md)
68+
- [RangeError](doc/range_error.md)
6769
- [Object Lifetime Management](doc/object_lifetime_management.md)
6870
- [HandleScope](doc/handle_scope.md)
6971
- [EscapableHandleScope](doc/escapable_handle_scope.md)

doc/async_operations.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Asynchronous operations
22

3-
You are reading a draft of the next documentation and it's in continuous update so
4-
if you don't find what you need please refer to:
5-
[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/)
3+
Node.js native add-ons often need to execute long running tasks and to avoid
4+
blocking the **event loop** they have to run them asynchronously from the
5+
**event loop**.
6+
In the Node.js model of execution the event loop thread represents the thread
7+
where JavaScript code is executing. The node.js guidance is to avoid blocking
8+
other work queued on the event loop thread. Therefore, we need to do this work on
9+
another thread.
10+
11+
All this means that native add-ons need to leverage async helpers from libuv as
12+
part of their implementation. This allows them to schedule work to be executed
13+
asynchronously so that their methods can return in advance of the work being
14+
completed.
15+
16+
Node Addon API provides an interface to support functions that cover
17+
the most common asynchronous use cases. There is an abstract classes to implement
18+
asynchronous operations:
19+
20+
- **[AsyncWorker](async_worker.md)**
21+
22+
These class helps manage asynchronous operations through an abstraction
23+
of the concept of moving data between the **event loop** and **worker threads**.

doc/async_worker.md

Lines changed: 305 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,306 @@
1-
# Async worker
1+
# AsyncWorker
22

3-
You are reading a draft of the next documentation and it's in continuous update so
4-
if you don't find what you need please refer to:
5-
[C++ wrapper classes for the ABI-stable C APIs for Node.js](https://nodejs.github.io/node-addon-api/)
3+
`AsyncWorker` is an abstract class that you can subclass to remove many of the
4+
tedious tasks of moving data between the event loop and worker threads. This
5+
class internally handles all the details of creating and executing an asynchronous
6+
operation.
7+
8+
Once created, execution is requested by calling `Queue`. When a thread is
9+
available for execution the `Execute` method will be invoked. Once `Execute`
10+
complets either `OnOK` or `OnError` will be invoked. Once the `OnOK` or
11+
`OnError` methods are complete the AsyncWorker instance is destructed.
12+
13+
For the most basic use, only the `Execute` method must be implemented in a
14+
subclass.
15+
16+
## Methods
17+
18+
### Env
19+
20+
Requests the environment in which the async worker has been initially created.
21+
22+
```cpp
23+
Env Env() const;
24+
```
25+
26+
Returns the environment in which the async worker has been created.
27+
28+
### Queue
29+
30+
Requests that the work be queued for execution.
31+
32+
```cpp
33+
void Queue();
34+
```
35+
36+
### Cancel
37+
38+
Cancels queued work if it has not yet been started. If it has already started
39+
executing, it cannot be cancelled. If cancelled successfully neither
40+
`OnOK` nor `OnError` will be called.
41+
42+
```cpp
43+
void Cancel();
44+
```
45+
46+
### Receiver
47+
48+
```cpp
49+
ObjectReference& Receiver();
50+
```
51+
52+
Returns the persistent object reference of the receiver object set when the async
53+
worker was created.
54+
55+
### Callback
56+
57+
```cpp
58+
FunctionReference& Callback();
59+
```
60+
61+
Returns the persistent function reference of the callback set when the async
62+
worker was created. The returned function reference will receive the results of
63+
the computation that happened in the `Execute` method, unless the default
64+
implementation of `OnOK` or `OnError` is overridden.
65+
66+
### SetError
67+
68+
Sets the error message for the error that happened during the execution. Setting
69+
an error message will cause the `OnError` method to be invoked instead of `OnOK`
70+
once the `Execute` method completes.
71+
72+
```cpp
73+
void SetError(const std::string& error);
74+
```
75+
76+
- `[in] error`: The reference to the string that represent the message of the error.
77+
78+
### Execute
79+
80+
This method is used to execute some tasks out of the **event loop** on a libuv
81+
worker thread. Subclasses must implement this method and the method is run on
82+
a thread other than that running the main event loop. As the method is not
83+
running on the main event loop, it must avoid calling any methods from node-addon-api
84+
or running any code that might invoke JavaScript. Instead once this method is
85+
complete any interaction through node-addon-api with JavaScript should be implemented
86+
in the `OnOK` method which runs on the main thread and is invoked when the `Execute`
87+
method completes.
88+
89+
```cpp
90+
virtual void Execute() = 0;
91+
```
92+
93+
### OnOK
94+
95+
This method is invoked when the computation in the `Excecute` method ends.
96+
The default implementation runs the Callback provided when the AsyncWorker class
97+
was created.
98+
99+
```cpp
100+
virtual void OnOK();
101+
```
102+
103+
### OnError
104+
105+
This method is invoked afer Execute() completes if an error occurs
106+
while `Execute` is running and C++ exceptions are enabled or if an
107+
error was set through a call to `SetError`. The default implementation
108+
calls the callback provided when the AsyncWorker class was created, passing
109+
in the error as the first parameter.
110+
111+
```cpp
112+
virtual void OnError(const Error& e);
113+
```
114+
115+
### Constructor
116+
117+
Creates a new `AsyncWorker`.
118+
119+
```cpp
120+
explicit AsyncWorker(const Function& callback);
121+
```
122+
123+
- `[in] callback`: The function which will be called when an asynchronous
124+
operations ends. The given function is called from the main event loop thread.
125+
126+
Returns an AsyncWork instance which can later be queued for execution by calling
127+
`Queue`.
128+
129+
### Constructor
130+
131+
Creates a new `AsyncWorker`.
132+
133+
```cpp
134+
explicit AsyncWorker(const Function& callback, const char* resource_name);
135+
```
136+
137+
- `[in] callback`: The function which will be called when an asynchronous
138+
operations ends. The given function is called from the main event loop thread.
139+
- `[in] resource_name`: Null-terminated strings that represents the
140+
identifier for the kind of resource that is being provided for diagnostic
141+
information exposed by the async_hooks API.
142+
143+
Returns an AsyncWork instance which can later be queued for execution by calling
144+
`Queue`.
145+
146+
147+
### Constructor
148+
149+
Creates a new `AsyncWorker`.
150+
151+
```cpp
152+
explicit AsyncWorker(const Function& callback, const char* resource_name, const Object& resource);
153+
```
154+
155+
- `[in] callback`: The function which will be called when an asynchronous
156+
operations ends. The given function is called from the main event loop thread.
157+
- `[in] resource_name`: Null-terminated strings that represents the
158+
identifier for the kind of resource that is being provided for diagnostic
159+
information exposed by the async_hooks API.
160+
- `[in] resource`: Object associated with the asynchronous operation that
161+
will be passed to possible async_hooks.
162+
163+
Returns an AsyncWork instance which can later be queued for execution by calling
164+
`Queue`.
165+
166+
### Constructor
167+
168+
Creates a new `AsyncWorker`.
169+
170+
```cpp
171+
explicit AsyncWorker(const Object& receiver, const Function& callback);
172+
```
173+
174+
- `[in] receiver`: The `this` object passed to the called function.
175+
- `[in] callback`: The function which will be called when an asynchronous
176+
operations ends. The given function is called from the main event loop thread.
177+
178+
Returns an AsyncWork instance which can later be queued for execution by calling
179+
`Queue`.
180+
181+
182+
### Constructor
183+
184+
Creates a new `AsyncWorker`.
185+
186+
```cpp
187+
explicit AsyncWorker(const Object& receiver, const Function& callback,const char* resource_name);
188+
```
189+
190+
- `[in] receiver`: The `this` object passed to the called function.
191+
- `[in] callback`: The function which will be called when an asynchronous
192+
operations ends. The given function is called from the main event loop thread.
193+
- `[in] resource_name`: Null-terminated strings that represents the
194+
identifier for the kind of resource that is being provided for diagnostic
195+
information exposed by the async_hooks API.
196+
197+
Returns an AsyncWork instance which can later be queued for execution by calling
198+
`Queue`.
199+
200+
201+
### Constructor
202+
203+
Creates a new `AsyncWorker`.
204+
205+
```cpp
206+
explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource);
207+
```
208+
209+
- `[in] receiver`: The `this` object passed to the called function.
210+
- `[in] callback`: The function which will be called when an asynchronous
211+
operations ends. The given function is called from the main event loop thread.
212+
- `[in] resource_name`: Null-terminated strings that represents the
213+
identifier for the kind of resource that is being provided for diagnostic
214+
information exposed by the async_hooks API.
215+
- `[in] resource`: Object associated with the asynchronous operation that
216+
will be passed to possible async_hooks.
217+
218+
Returns an AsyncWork instance which can later be queued for execution by calling
219+
`Queue`.
220+
221+
### Destructor
222+
223+
Deletes the created work object that is used to execute logic asynchronously.
224+
225+
```cpp
226+
virtual ~AsyncWorker();
227+
```
228+
229+
## Operator
230+
231+
```cpp
232+
operator napi_async_work() const;
233+
```
234+
235+
Returns the N-API napi_async_work wrapped by the AsyncWorker object. This can be
236+
used to mix usage of the C N-API and node-addon-api.
237+
238+
## Example
239+
240+
The first step to use the `AsyncWorker` class is to create a new class that inherit
241+
from it and implement the `Execute` abstract method. Typically input to your
242+
worker will be saved within class' fields generally passed in through its
243+
constructor.
244+
245+
When the `Execute` method completes without errors the `OnOK` function callback
246+
will be invoked. In this function the results of the computation will be
247+
reassembled and returned back to the initial JavaScript context.
248+
249+
`AsyncWorker` ensures that all the code in the `Execute` function runs in the
250+
background out of the **event loop** thread and at the end the `OnOK` or `OnError`
251+
function will be called and are executed as part of the event loop.
252+
253+
The code below show a basic example of `AsyncWorker` the implementation:
254+
255+
```cpp
256+
#include<napi.h>
257+
258+
#include <chrono>
259+
#include <thread>
260+
261+
use namespace Napi;
262+
263+
class EchoWorker : public AsyncWorker {
264+
public:
265+
EchoWorker(Function& callback, std::string& echo)
266+
: AsyncWorker(callback), echo(echo) {}
267+
268+
~EchoWorker() {}
269+
// This code will be executed on the worker thread
270+
void Execute() {
271+
// Need to simulate cpu heavy task
272+
std::this_thread::sleep_for(std::chrono::seconds(1));
273+
}
274+
275+
void OnOK() {
276+
HandleScope scope(Env());
277+
Callback().Call({Env().Null(), String::New(Env(), echo)});
278+
}
279+
280+
private:
281+
std::string echo;
282+
};
283+
```
284+
285+
The `EchoWorker`'s contructor calls the base class' constructor to pass in the
286+
callback that the `AsyncWorker` base class will store persistently. When the work
287+
on the `Execute` method is done the `OnOk` method is called and the results return
288+
back to JavaScript invoking the stored callback with its associated environment.
289+
290+
The following code shows an example on how to create and and use an `AsyncWorker`
291+
292+
```cpp
293+
Value Echo(const CallbackInfo& info) {
294+
// You need to check the input data here
295+
Function cb = info[1].As<Function>();
296+
std::string in = info[0].As<String>();
297+
EchoWorker* wk = new EchoWorker(cb, in);
298+
wk->Queue();
299+
return info.Env().Undefined();
300+
```
301+
302+
Using the implementation of an `AsyncWorker` is straight forward. You need only create
303+
a new instance and pass to its constructor the callback you want to execute when
304+
your asynchronous task ends and other data you need for your computation. Once created the
305+
only other action you have to do is to call the `Queue` method that will that will
306+
queue the created worker for execution.

0 commit comments

Comments
 (0)