Skip to content

Commit 1b52c28

Browse files
authored
Clean up AsyncProgressWorker documentation (#831)
* Clean up AsyncProgressWorker documentation The old documentation was not very clear on how to use the API. It was missing javascript references, and was not necessarily following the best coding standards. These adjustments make it a bit more complete now by providing all three necessary parts, the worker, the hookup code, and the javascript usage of it. I have also gone ahead and rewritten the `AsyncProgressQueueWorker` example to show demonstration of the `FunctionReference` class and how to use multiple callbacks instead of one combined callback.
1 parent c9563ca commit 1b52c28

File tree

1 file changed

+141
-40
lines changed

1 file changed

+141
-40
lines changed

doc/async_worker_variants.md

Lines changed: 141 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,9 @@ be safely released.
243243
Note that `Napi::AsyncProgressWorker::ExecutionProcess::Send` merely guarantees
244244
**eventual** invocation of `Napi::AsyncProgressWorker::OnProgress`, which means
245245
multiple send might be coalesced into single invocation of `Napi::AsyncProgressWorker::OnProgress`
246-
with latest data.
246+
with latest data. If you would like to guarantee that there is one invocation of
247+
`OnProgress` for every `Send` call, you should use the `Napi::AsyncProgressQueueWorker`
248+
class instead which is documented further down this page.
247249

248250
```cpp
249251
void Napi::AsyncProgressWorker::ExecutionProcess::Send(const T* data, size_t count) const;
@@ -269,7 +271,8 @@ function runs in the background out of the **event loop** thread and at the end
269271
the `Napi::AsyncProgressWorker::OnOK` or `Napi::AsyncProgressWorker::OnError` function will be
270272
called and are executed as part of the event loop.
271273
272-
The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation:
274+
The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation along with an
275+
example of how the counterpart in Javascript would appear:
273276
274277
```cpp
275278
#include <napi.h>
@@ -281,28 +284,38 @@ using namespace Napi;
281284
282285
class EchoWorker : public AsyncProgressWorker<uint32_t> {
283286
public:
284-
EchoWorker(Function& callback, std::string& echo)
285-
: AsyncProgressWorker(callback), echo(echo) {}
287+
EchoWorker(Function& okCallback, std::string& echo)
288+
: AsyncProgressWorker(okCallback), echo(echo) {}
286289
287290
~EchoWorker() {}
288-
// This code will be executed on the worker thread
289-
void Execute(const ExecutionProgress& progress) {
290-
// Need to simulate cpu heavy task
291-
for (uint32_t i = 0; i < 100; ++i) {
292-
progress.Send(&i, 1)
293-
std::this_thread::sleep_for(std::chrono::seconds(1));
291+
292+
// This code will be executed on the worker thread
293+
void Execute(const ExecutionProgress& progress) {
294+
// Need to simulate cpu heavy task
295+
// Note: This Send() call is not guaranteed to trigger an equal
296+
// number of OnProgress calls (read documentation above for more info)
297+
for (uint32_t i = 0; i < 100; ++i) {
298+
progress.Send(&i, 1)
299+
}
300+
}
301+
302+
void OnError(const Error &e) {
303+
HandleScope scope(Env());
304+
// Pass error onto JS, no data for other parameters
305+
Callback().Call({String::New(Env(), e.Message())});
294306
}
295-
}
296307
297-
void OnOK() {
298-
HandleScope scope(Env());
299-
Callback().Call({Env().Null(), String::New(Env(), echo)});
300-
}
308+
void OnOK() {
309+
HandleScope scope(Env());
310+
// Pass no error, give back original data
311+
Callback().Call({Env().Null(), String::New(Env(), echo)});
312+
}
301313
302-
void OnProgress(const uint32_t* data, size_t /* count */) {
303-
HandleScope scope(Env());
304-
Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)});
305-
}
314+
void OnProgress(const uint32_t* data, size_t /* count */) {
315+
HandleScope scope(Env());
316+
// Pass no error, no echo data, but do pass on the progress data
317+
Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)});
318+
}
306319
307320
private:
308321
std::string echo;
@@ -327,12 +340,23 @@ using namespace Napi;
327340

328341
Value Echo(const CallbackInfo& info) {
329342
// We need to validate the arguments here
330-
Function cb = info[1].As<Function>();
331343
std::string in = info[0].As<String>();
344+
Function cb = info[1].As<Function>();
332345
EchoWorker* wk = new EchoWorker(cb, in);
333346
wk->Queue();
334347
return info.Env().Undefined();
335348
}
349+
350+
// Register the native method for JS to access
351+
Object Init(Env env, Object exports)
352+
{
353+
exports.Set(String::New(env, "echo"), Function::New(env, Echo));
354+
355+
return exports;
356+
}
357+
358+
// Register our native addon
359+
NODE_API_MODULE(nativeAddon, Init)
336360
```
337361
338362
The implementation of a `Napi::AsyncProgressWorker` can be used by creating a
@@ -341,6 +365,20 @@ asynchronous task ends and other data needed for the computation. Once created,
341365
the only other action needed is to call the `Napi::AsyncProgressWorker::Queue`
342366
method that will queue the created worker for execution.
343367
368+
Lastly, the following Javascript (ES6+) code would be associated the above example:
369+
370+
```js
371+
const { nativeAddon } = require('binding.node');
372+
373+
const exampleCallback = (errorResponse, okResponse, progressData) => {
374+
// Use the data accordingly
375+
// ...
376+
};
377+
378+
// Call our native addon with the paramters of a string and a function
379+
nativeAddon.echo("example", exampleCallback);
380+
```
381+
344382
# AsyncProgressQueueWorker
345383

346384
`Napi::AsyncProgressQueueWorker` acts exactly like `Napi::AsyncProgressWorker`
@@ -379,7 +417,9 @@ void Napi::AsyncProgressQueueWorker::ExecutionProcess::Send(const T* data, size_
379417
380418
## Example
381419
382-
The code below shows a basic example of the `Napi::AsyncProgressQueueWorker` implementation:
420+
The code below show an example of the `Napi::AsyncProgressQueueWorker` implementation, but
421+
also demonsrates how to use multiple `Napi::Function`'s if you wish to provide multiple
422+
callback functions for more object oriented code:
383423
384424
```cpp
385425
#include <napi.h>
@@ -391,31 +431,55 @@ using namespace Napi;
391431
392432
class EchoWorker : public AsyncProgressQueueWorker<uint32_t> {
393433
public:
394-
EchoWorker(Function& callback, std::string& echo)
395-
: AsyncProgressQueueWorker(callback), echo(echo) {}
434+
EchoWorker(Function& okCallback, Function& errorCallback, Function& progressCallback, std::string& echo)
435+
: AsyncProgressQueueWorker(okCallback), echo(echo) {
436+
// Set our function references to use them below
437+
this->errorCallback.Reset(errorCallback, 1);
438+
this->progressCallback.Reset(progressCallback, 1);
439+
}
396440
397441
~EchoWorker() {}
398-
// This code will be executed on the worker thread
399-
void Execute(const ExecutionProgress& progress) {
400-
// Need to simulate cpu heavy task
401-
for (uint32_t i = 0; i < 100; ++i) {
402-
progress.Send(&i, 1);
403-
std::this_thread::sleep_for(std::chrono::seconds(1));
442+
443+
// This code will be executed on the worker thread
444+
void Execute(const ExecutionProgress& progress) {
445+
// Need to simulate cpu heavy task to demonstrate that
446+
// every call to Send() will trigger an OnProgress function call
447+
for (uint32_t i = 0; i < 100; ++i) {
448+
progress.Send(&i, 1);
449+
}
404450
}
405-
}
406451
407-
void OnOK() {
408-
HandleScope scope(Env());
409-
Callback().Call({Env().Null(), String::New(Env(), echo)});
410-
}
452+
void OnOK() {
453+
HandleScope scope(Env());
454+
// Call our onOkCallback in javascript with the data we were given originally
455+
Callback().Call({String::New(Env(), echo)});
456+
}
457+
458+
void OnError(const Error &e) {
459+
HandleScope scope(Env());
460+
461+
// We call our callback provided in the constructor with 2 parameters
462+
if (!this->errorCallback.IsEmpty()) {
463+
// Call our onErrorCallback in javascript with the error message
464+
this->errorCallback.Call(Receiver().Value(), {String::New(Env(), e.Message())});
465+
}
466+
}
411467
412-
void OnProgress(const uint32_t* data, size_t /* count */) {
413-
HandleScope scope(Env());
414-
Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)});
415-
}
468+
void OnProgress(const uint32_t* data, size_t /* count */) {
469+
HandleScope scope(Env());
470+
471+
if (!this->progressCallback.IsEmpty()) {
472+
// Call our onProgressCallback in javascript with each integer from 0 to 99 (inclusive)
473+
// as this function is triggered from the above Send() calls
474+
this->progressCallback.Call(Receiver().Value(), {Number::New(Env(), *data)});
475+
}
476+
}
416477
417478
private:
418479
std::string echo;
480+
FunctionReference progressCallback;
481+
FunctionReference errorCallback;
482+
419483
};
420484
```
421485

@@ -439,12 +503,25 @@ using namespace Napi;
439503

440504
Value Echo(const CallbackInfo& info) {
441505
// We need to validate the arguments here.
442-
Function cb = info[1].As<Function>();
443506
std::string in = info[0].As<String>();
444-
EchoWorker* wk = new EchoWorker(cb, in);
507+
Function errorCb = info[1].As<Function>();
508+
Function okCb = info[2].As<Function>();
509+
Function progressCb = info[3].As<Function>();
510+
EchoWorker* wk = new EchoWorker(okCb, errorCb, progressCb, in);
445511
wk->Queue();
446512
return info.Env().Undefined();
447513
}
514+
515+
// Register the native method for JS to access
516+
Object Init(Env env, Object exports)
517+
{
518+
exports.Set(String::New(env, "echo"), Function::New(env, Echo));
519+
520+
return exports;
521+
}
522+
523+
// Register our native addon
524+
NODE_API_MODULE(nativeAddon, Init)
448525
```
449526
450527
The implementation of a `Napi::AsyncProgressQueueWorker` can be used by creating a
@@ -453,4 +530,28 @@ asynchronous task ends and other data needed for the computation. Once created,
453530
the only other action needed is to call the `Napi::AsyncProgressQueueWorker::Queue`
454531
method that will queue the created worker for execution.
455532
533+
Lastly, the following Javascript (ES6+) code would be associated the above example:
534+
535+
```js
536+
const { nativeAddon } = require('binding.node');
537+
538+
const onErrorCallback = (msg) => {
539+
// Use the data accordingly
540+
// ...
541+
};
542+
543+
const onOkCallback = (echo) => {
544+
// Use the data accordingly
545+
// ...
546+
};
547+
548+
const onProgressCallback = (num) => {
549+
// Use the data accordingly
550+
// ...
551+
};
552+
553+
// Call our native addon with the paramters of a string and three callback functions
554+
nativeAddon.echo("example", onErrorCallback, onOkCallback, onProgressCallback);
555+
```
556+
456557
[`Napi::AsyncWorker`]: ./async_worker.md

0 commit comments

Comments
 (0)