The promise example shows how to create a deferred and return its promise, but I am not sure about how to handle it. In some place I need to store a pointer to the deferred. When I use the factory method and try to store it to a private member variable of my class I of course get
error: taking address of temporary. So I use the constructor with a new call, but then: when can I destroy the deferred? After resolving or rejecting it? Will the promise (which I returned to the javascript code) survive that?
Thank you for your clarification.
Hi @0815fox ,
Take a look at this WIP example:
Call() method is exposed to JavaScript and creates a new deferred, returning its promise back to Node.Does this example help? Let us know if you have additional questions.
Thanks, Kevin
Hi @KevinEady ,
thank you first for trying to help on this :-) Its not easy to keep up with this example as it is a bit complex. So let me show you a simplified example of what I am about to do (hint: I left out some details -> it's not working code):
class Foo : public Napi::AsyncWorker {
private:
Napi::Promise::Deferred * deferredPtr;
bool workDone;
bool stopped = false;
public:
Napi::Value doSomething();
void callbackFromExternalLibrary();
virtual void Execute();
...
}
Napi::Value Foo::doSomething() {
deferredPtr = new Napi::Promise::Deferred(Env());
workDone = false;
startSomethingInExternalLibrary();
return deferredPtr->Promise();
}
void Foo::callbackFromExternalLibrary() {
workDone = true;
}
void Foo::Execute() {
while(!stopped) {
callExternalLibraryWorker(); // -> this will eventually lead to callbackFromExternalLibrary being called.
if (workDone) {
deferredPtr->Re[solve|ject](...);
delete deferredPtr; // <- this is what my question is about...
deferredPtr = NULL;
}
}
}
So my AsyncWorker is running all the time as the external library needs a permanent worker call. doSomething is called from js which starts some action on that external library. On completion the callbackFromExternalLibrary is called from within the worker function. After that the promise gets resolved and:
Q: Close to the end I have a delete call there. The question is: is it okay to delete the deferred after Re[solve|ject]ing it or will this also destroy the promise thus causing internal trouble?
Thank you,
Michael
I think I found the answer: https://nodejs.org/api/n-api.html#n_api_promises :+1:
The deferred object that is created by napi_create_promise() is freed by napi_resolve_deferred() or napi_reject_deferred()
Hi @0815fox,
yes you are right, but you should not resolve or reject the promise in the Execute method.
See: https://github.com/nodejs/node-addon-api/blob/master/doc/async_worker.md#execute
In the specific you should use the OnOk and OnError methods to call on JavaScript (Main thread).
Hi @NickNaso,
This might be true, however, I cannot, as the promise has to be resolved while the async worker still continues its work. Else I need another suggestion on how to implement a class which executes a separate thread that periodically calls the worker of that third party library which I intend to integrate. I want to avoid calling the library worker from JavaScript regularly.
Somehow I am missing a simple introduction to Napi which tells me about the basic concepts. Somehow I easily get lost clueless what I am doing wrong.
So actually now when I am doing the above i get the following message:
#
# Fatal error in v8::HandleScope::CreateHandle()
# Cannot create a handle without a HandleScope
#
Illegal instruction
Any hints on what is causing this? It's happening when I resolve the promise. I mean you could now say that I shall resolve the promise from onOk but then I don't understand why this does not work. What's the concept behind?
So what I am reading now is Object Lifetime Management and I guess I should somehow use an "EscapableHandleScope".
EscapableHandleScope docs states:
For more details refer to the section titled Object lifetime management.
But in there I only see details for shortening the lifespan.
And here I get stuck: Okay, I can create an EscapableHandleScope. Do I have to store a pointer or reference to it somewhere and then destroy it later? If so: how? Or will it be destroyed automatically when all objects created in it have been destructed?
Renamed + reopened. BTW I found someone else struggeling on this: https://stackoverflow.com/questions/62431517/fatal-error-in-v8handlescopecreatehandle-cannot-create-a-handle-without
Hi @0815fox,
you cannot call on JavaScript from Napi::AsyncWorker::Execute method because you are on a different thread (not in the event loop or main thread) so in this method you can execute your task collect the result / error and then use the Napi::AsyncWorker::OnOK and Napi::AsyncWorker::OnError to return the result to JavaScript.
The main purpose of Napi::AsyncWorker is to provide a simple interface to execute task and do not block the main thread.
See: https://github.com/nodejs/node-addon-api/blob/master/doc/async_operations.md#asynchronous-operations
There is another variant for Napi::AsyncWorker called Napi::AsyncProgressWorker that give you the possibility to report to the main thread the progress of your operation.
If you need more flexibility you should use Napi::ThreadSafeFunction that was introduced to allow to call JavaScript on the main thread from other thread.
See: https://github.com/nodejs/node-addon-api/blob/master/doc/threadsafe_function.md#threadsafefunction
JavaScript functions can normally only be called from a native addon's main thread. If an addon creates additional threads, then node-addon-api functions that require a Napi::Env, Napi::Value, or Napi::Reference must not be called from those threads.
Thank you, that helps. Well slowly a picture is forming. Just to explain why I struggled:
I first read https://github.com/nodejs/node-addon-api/blob/master/doc/async_operations.md#asynchronous-operations chapter. The hint on the bottom of this class is a bit vague and so I stepped into https://github.com/nodejs/node-addon-api/blob/master/doc/async_worker.md. There was also no hint on the limitation, so (as a beginner) I do not see that I cannot make JavaScript callbacks from within the Execute method. Even deeper: I did not want to make JavaScript callbacks in the first place, I wanted to resolve a promise which I did not see as a JavaScript callback (as the microtasks are executed asynchronously anyway).
Finally the chapter name ThreadSafeFunction did not gather my interest because I wanted to work with promises. And the chapter https://github.com/nodejs/node-addon-api/blob/master/doc/promises.md does not state that the Resolve method cannot be called from a different thread.
-> Whats the correct way to create a ThreadSafeDeferred on the C++ side, return its promise and resolve it from another thread? I mean this might be quite necessary, as the purpose of promises is to work asynchronously.
I mean all I want to achieve is: have a seperate thread and resolve a promise from there - "can't be that hard" is what I'm thinking...? Or is there a way to call a c function in the main thread which then resolves the promise for me?
Ah and CallbackScope and AsyncContext: From the docs I simply don't get the idea on what to do with them in concrete...
As a "WorkAround" I will have to implement a PromiseResolutionWorker which extends AsyncWorker solely for the purpose of resolving a promise from the onOk callback.
Can instances of basic types (e.g. Napi::Boolean, Number, String) be created outside of the main thread? Okay from the threadsafe function example it seems like yes.
Is there an overview or a general rule on what you can/can't do outside of the main thread?
Sorry for the long message, but I thought a head-dump would help to see where a beginner user of your code might struggle.
Okay, I have implemented a module napi-threadsafe-deferred.
Actually there already exists a module napi_thread_safe_promise, but it does not do what I want to achieve - it seems to be more like a kind of async worker with promise support. But surely this one as well as your hints and code samples helped me to implement this.
@NickNaso @KevinEady
I'd like to invite you to review my code and give feedback before publishing it.
Also I have to revise my statement from above:
I think I found the answer: https://nodejs.org/api/n-api.html#n_api_promises +1
The deferred object that is created by napi_create_promise() is freed by napi_resolve_deferred() or napi_reject_deferred()
The underlying promise is deleted automatically. The Napi::Promise::Deferred however is not deleted automatically.
@0815fox the Napi::Promise::Deferred becomes unusable after it is resolved/rejected. Thus, after calling Resolve or Reject, if the Napi::Promise::Deferred is heap-allocated it can safely be destroyed, and must actually be destroyed in order to prevent a leak.
@NickNaso please add some text to https://github.com/nodejs/node-addon-api/blame/master/doc/promises.md#L59 at the Resolve() and Reject() methods explaining that the Napi::Promise::Deferred cannot be used after these calls, and that, if it is heap-allocated, it should be destroyed at that point.
Thank you for the feedback. I totally agree, that memory leak is a pitfall for anyone using the C++ wrapper.
Lack of feedback I now published napi-threadsafe-deferred - feedback may be placed as tickets there.
I'm still unsure, if it's acceptable behaviour that the instance deletes itself within a try/catch block. While it's not nice, it's even worse to leave deletion up to the user as the point in time where it can safely be deleted is not detectable by the user. Maybe I should make this configurable?
This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.
Most helpful comment
Hi @0815fox,
you cannot call on JavaScript from
Napi::AsyncWorker::Executemethod because you are on a different thread (not in the event loop or main thread) so in this method you can execute your task collect the result / error and then use theNapi::AsyncWorker::OnOKandNapi::AsyncWorker::OnErrorto return the result to JavaScript.The main purpose of
Napi::AsyncWorkeris to provide a simple interface to execute task and do not block the main thread.See: https://github.com/nodejs/node-addon-api/blob/master/doc/async_operations.md#asynchronous-operations
There is another variant for
Napi::AsyncWorkercalledNapi::AsyncProgressWorkerthat give you the possibility to report to the main thread the progress of your operation.If you need more flexibility you should use
Napi::ThreadSafeFunctionthat was introduced to allow to call JavaScript on the main thread from other thread.See: https://github.com/nodejs/node-addon-api/blob/master/doc/threadsafe_function.md#threadsafefunction