Giter Club home page Giter Club logo

Comments (13)

jasongin avatar jasongin commented on July 27, 2024 3

If you have some result data to pass back from Execute(), I'd suggest saving it to a member variable of your AsyncWorker subclass. Then in your OnOK() override you can pass that result data back to a JavaScript function.

I suppose we could add to the library something like an AsyncWorker<TResult> class, that has a TResult Result() and maybe a SetResult(TResult result) method. But I don't know if it would very helpful since you need to subclass AsyncWorker anyway as it is to override the OnOK() method. So it's trivial to then define your own result member(s) when needed.

from node-addon-api.

jasongin avatar jasongin commented on July 27, 2024 3

The _test value is invalid by the time OnOK() runs because it got collected by the GC. To prevent that, you should store it as a Napi::ObjectReference:

private:
  Napi::ObjectReference _test;
...
_test = Napi::Persistent(Napi::Object::New(Env()));

Or you can use the Receiver() ObjectReference that is already attached to the AsyncWorker instance, and set/get properties on it:

Receiver().Set("test", Napi::Object::New(Env());

from node-addon-api.

jasongin avatar jasongin commented on July 27, 2024 3

Oh, yes I forgot to mention an important detail. The Execute() method does not run in a JavaScript context, so you can't access any JS types from there. Instead you should save any results as (non-JS) member variables, and convert the results to JS values in the OkOK() callback.

from node-addon-api.

krystianity avatar krystianity commented on July 27, 2024 2

Sorry for adding momentum to a closed issue, but I was trying to implement the suggested solution that @jasongin mentioned above. Using non Napi::Object values like Napi::Boolean works fine, but I am trying to call the callback function with an object as second parameter and it results in segfaults and funky promises resolving in error messages 💃

Any idea what I (not that strong at C++) am doing wrong here?

// callme.cc
#include "napi.h"

namespace hello {

    class HelloWorker : public Napi::AsyncWorker {

    public:

        HelloWorker(Napi::Function callback) : AsyncWorker(callback){
            _test = Napi::Object::New(Env());
        }

        bool _succeed;
        
    protected:
        
        void Execute() override {
            if(!_succeed){
                SetError("Hello World Error");
            } else {
                //segfault here
               _test.Set("test", Napi::Boolean::New(Env(), true));
            }
        }

    private:

        Napi::Object _test; 

        void OnOK() override {
            Callback().MakeCallback(Receiver().Value(), std::initializer_list<napi_value>{
               Napi::Value(), //segfault
               _test //promise with: Invalid pointer passed as argument
            });
        }
    };

    void RunCallback(const Napi::CallbackInfo& info){

        bool succeed = info[0].As<Napi::Boolean>();
        Napi::Value data = info[1];
        Napi::Function callback = info[2].As<Napi::Function>();

        HelloWorker* worker = new HelloWorker(callback);
        worker->Receiver().Set("data", data);
        worker->_succeed = succeed;
        worker->Queue();
    }

    Napi::Function InitCallMe(Napi::Env env){
        Napi::Function runcallback_function = Napi::Function::New(env, RunCallback);
        return runcallback_function;
    }
} //namespace hello

from node-addon-api.

krystianity avatar krystianity commented on July 27, 2024 1

Thanks for the super fast reply @jasongin , passing the object as callback param works now.
But I still cannot do any actions inside of Execute()

Receiver().Get("any"); is a segfault already :(

// callme.cc
#include "napi.h"

namespace hello {

    const char* RESULT_KEY = "result";

    //AsyncWorker is an abstract helper class for thread-pooled tasks
    class HelloWorker : public Napi::AsyncWorker {

    public:

        HelloWorker(Napi::Function callback) : AsyncWorker(callback){
            Receiver().Set(RESULT_KEY, Napi::Object::New(Env()));
        }

        bool _succeed;
        
    protected:
        
        //called when first in queue and dispatched in thread
        void Execute() override {

            if(!_succeed){
                SetError("Hello World Error");
                return;
            }

            Receiver().Get("any"); //segfault
                
            /*
            Napi::Value value = Receiver().Get(RESULT_KEY);
            Napi::Object result = value.As<Napi::Object>();
            result.Set("test", Napi::Boolean::New(Env(), true)); */
        }

    private:

        //called when Execute() does not call SetError()
        void OnOK() override {
            Callback().MakeCallback(Receiver().Value(), std::initializer_list<napi_value>{
                Env().Null(),
                Receiver().Get(RESULT_KEY)
            });
        }
    };

    void RunCallback(const Napi::CallbackInfo& info){

        bool succeed = info[0].As<Napi::Boolean>();
        Napi::Value data = info[1];
        Napi::Function callback = info[2].As<Napi::Function>();

        HelloWorker* worker = new HelloWorker(callback);
        worker->Receiver().Set("data", data);
        worker->_succeed = succeed;
        worker->Queue();
    }

    Napi::Function InitCallMe(Napi::Env env){
        Napi::Function runcallback_function = Napi::Function::New(env, RunCallback);
        return runcallback_function;
    }
} //namespace hello

from node-addon-api.

krystianity avatar krystianity commented on July 27, 2024 1

@jasongin thanks for the hint 💯 got it now :)

from node-addon-api.

ptgolden avatar ptgolden commented on July 27, 2024 1

If JavaScript types can only be accessed in the OnOK callback, that means that AsyncWorker cannot handle progressively calling a JS function, correct? For example, calling an onData callback each time a new piece of information is available.

(I'm trying to write an addon that produces (or interfaces with) a Node readable stream).

from node-addon-api.

jasongin avatar jasongin commented on July 27, 2024

When using the C APIs, the void* data parameter is the way to do it, as you described. The napi_status parameter to the napi_async_complete_callback is not intended to be an indication of whether the execution callback was successful, merely whether the execution was scheduled and ran (or was cancelled).

However, I'd recommend looking at the AsyncWorker class in the C++ API instead. Async callbacks can be tricky to get correct, and the higher level of abstraction provided by the AsyncWorker class can simplify things a lot. For example it has a SetError() method that's meant to be called during execution, which causes a different completion callback to run: OnError() instead of OnOK(); the default implementation of OnError() invokes the JS callback function with an Error value.

Unfortunately the documentation for that class isn't written yet. (Would anyone like to help?)

from node-addon-api.

skibz avatar skibz commented on July 27, 2024

thanks @jasongin !
i'll have a crack at AsyncWorker 💃

from node-addon-api.

koistya avatar koistya commented on July 27, 2024

@jasongin How to pass "data" back to the caller from under the Execute() method?

from node-addon-api.

gorshkov-leonid avatar gorshkov-leonid commented on July 27, 2024

@jasongin could you reply to @ptgolden's question? I can find neither node-addon-api or n-api functions to implement streaming worker such as Nan::AsyncProgressWorker

There is example of usage from a book: [factorization example | https://github.com/freezer333/nodecpp-demo/blob/master/streaming/examples/factorization/factorization.js].

In my case I need to hook some system events and pass them to js callback.

from node-addon-api.

mhdawson avatar mhdawson commented on July 27, 2024

@gorshkov-leonid can you open a new issue. This one is closed and its probably better to discuss in a new issue.

from node-addon-api.

MD-AZMAL avatar MD-AZMAL commented on July 27, 2024

The _test value is invalid by the time OnOK() runs because it got collected by the GC. To prevent that, you should store it as a Napi::ObjectReference:

private:
  Napi::ObjectReference _test;
...
_test = Napi::Persistent(Napi::Object::New(Env()));

Or you can use the Receiver() ObjectReference that is already attached to the AsyncWorker instance, and set/get properties on it:

Receiver().Set("test", Napi::Object::New(Env());

I am trying something similar but it gives me an issue saying .Set is not a function

from node-addon-api.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.