Skip to content

Commit 667b0ad

Browse files
author
John French
committed
AsyncWorker: add GetResult() method
Adds an overridable `GetResult()` method, providing arguments to the callback invoked in `OnOK()`. Refs: nodejs/node-addon-api#231 (comment) PR-URL: nodejs/node-addon-api#512 Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: NickNaso <nicoladelgobbo@gmail.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
1 parent baec31c commit 667b0ad

File tree

5 files changed

+86
-3
lines changed

5 files changed

+86
-3
lines changed

doc/async_worker.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,21 @@ virtual void Napi::AsyncWorker::Execute() = 0;
107107

108108
This method is invoked when the computation in the `Execute` method ends.
109109
The default implementation runs the Callback optionally provided when the AsyncWorker class
110-
was created.
110+
was created. The callback will by default receive no arguments. To provide arguments,
111+
override the `GetResult()` method.
111112

112113
```cpp
113114
virtual void Napi::AsyncWorker::OnOK();
114115
```
116+
### GetResult
117+
118+
This method returns the arguments passed to the Callback invoked by the default
119+
`OnOK()` implementation. The default implementation returns an empty vector,
120+
providing no arguments to the Callback.
121+
122+
```cpp
123+
virtual std::vector<napi_value> Napi::AsyncWorker::GetResult(Napi::Env env);
124+
```
115125
116126
### OnError
117127

napi-inl.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3686,7 +3686,7 @@ inline void AsyncWorker::SuppressDestruct() {
36863686

36873687
inline void AsyncWorker::OnOK() {
36883688
if (!_callback.IsEmpty()) {
3689-
_callback.Call(_receiver.Value(), std::initializer_list<napi_value>{});
3689+
_callback.Call(_receiver.Value(), GetResult(_callback.Env()));
36903690
}
36913691
}
36923692

@@ -3700,7 +3700,14 @@ inline void AsyncWorker::SetError(const std::string& error) {
37003700
_error = error;
37013701
}
37023702

3703-
inline void AsyncWorker::OnExecute(napi_env /*env*/, void* this_pointer) {
3703+
inline std::vector<napi_value> AsyncWorker::GetResult(Napi::Env /*env*/) {
3704+
return {};
3705+
}
3706+
// The OnExecute method receives an napi_env argument. However, do NOT
3707+
// use it within this method, as it does not run on the main thread and must
3708+
// not run any method that would cause JavaScript to run. In practice, this
3709+
// means that almost any use of napi_env will be incorrect.
3710+
inline void AsyncWorker::OnExecute(napi_env /*DO_NOT_USE*/, void* this_pointer) {
37043711
AsyncWorker* self = static_cast<AsyncWorker*>(this_pointer);
37053712
#ifdef NAPI_CPP_EXCEPTIONS
37063713
try {

napi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,7 @@ namespace Napi {
18121812
virtual void OnOK();
18131813
virtual void OnError(const Error& e);
18141814
virtual void Destroy();
1815+
virtual std::vector<napi_value> GetResult(Napi::Env env);
18151816

18161817
void SetError(const std::string& error);
18171818

test/asyncworker.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,38 @@ class TestWorker : public AsyncWorker {
2929
bool _succeed;
3030
};
3131

32+
class TestWorkerWithResult : public AsyncWorker {
33+
public:
34+
static void DoWork(const CallbackInfo& info) {
35+
bool succeed = info[0].As<Boolean>();
36+
Object resource = info[1].As<Object>();
37+
Function cb = info[2].As<Function>();
38+
Value data = info[3];
39+
40+
TestWorkerWithResult* worker = new TestWorkerWithResult(cb, "TestResource", resource);
41+
worker->Receiver().Set("data", data);
42+
worker->_succeed = succeed;
43+
worker->Queue();
44+
}
45+
46+
protected:
47+
void Execute() override {
48+
if (!_succeed) {
49+
SetError("test error");
50+
}
51+
}
52+
53+
std::vector<napi_value> GetResult(Napi::Env env) override {
54+
return {Boolean::New(env, _succeed),
55+
String::New(env, _succeed ? "ok" : "error")};
56+
}
57+
58+
private:
59+
TestWorkerWithResult(Function cb, const char* resource_name, const Object& resource)
60+
: AsyncWorker(cb, resource_name, resource) {}
61+
bool _succeed;
62+
};
63+
3264
class TestWorkerNoCallback : public AsyncWorker {
3365
public:
3466
static Value DoWork(const CallbackInfo& info) {
@@ -65,5 +97,6 @@ Object InitAsyncWorker(Env env) {
6597
Object exports = Object::New(env);
6698
exports["doWork"] = Function::New(env, TestWorker::DoWork);
6799
exports["doWorkNoCallback"] = Function::New(env, TestWorkerNoCallback::DoWork);
100+
exports["doWorkWithResult"] = Function::New(env, TestWorkerWithResult::DoWork);
68101
return exports;
69102
}

test/asyncworker.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ function test(binding) {
6666
assert.strictEqual(typeof this, 'object');
6767
assert.strictEqual(this.data, 'test data');
6868
}, 'test data');
69+
70+
binding.asyncworker.doWorkWithResult(true, {}, function (succeed, succeedString) {
71+
assert(arguments.length == 2);
72+
assert(succeed);
73+
assert(succeedString == "ok");
74+
assert.strictEqual(typeof this, 'object');
75+
assert.strictEqual(this.data, 'test data');
76+
}, 'test data');
6977
return;
7078
}
7179

@@ -91,6 +99,30 @@ function test(binding) {
9199
}).catch(common.mustNotCall());
92100
}
93101

102+
{
103+
const hooks = installAsyncHooksForTest();
104+
const triggerAsyncId = async_hooks.executionAsyncId();
105+
binding.asyncworker.doWorkWithResult(true, { foo: 'foo' }, function (succeed, succeedString) {
106+
assert(arguments.length == 2);
107+
assert(succeed);
108+
assert(succeedString == "ok");
109+
assert.strictEqual(typeof this, 'object');
110+
assert.strictEqual(this.data, 'test data');
111+
}, 'test data');
112+
113+
hooks.then(actual => {
114+
assert.deepStrictEqual(actual, [
115+
{ eventName: 'init',
116+
type: 'TestResource',
117+
triggerAsyncId: triggerAsyncId,
118+
resource: { foo: 'foo' } },
119+
{ eventName: 'before' },
120+
{ eventName: 'after' },
121+
{ eventName: 'destroy' }
122+
]);
123+
}).catch(common.mustNotCall());
124+
}
125+
94126
{
95127
const hooks = installAsyncHooksForTest();
96128
const triggerAsyncId = async_hooks.executionAsyncId();

0 commit comments

Comments
 (0)