Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct way to use EventEmitter and emit events from addon #110

Closed
benfuu opened this issue Aug 17, 2017 · 14 comments
Closed

Correct way to use EventEmitter and emit events from addon #110

benfuu opened this issue Aug 17, 2017 · 14 comments

Comments

@benfuu
Copy link

benfuu commented Aug 17, 2017

So I've been looking around and see many different ways people have tried to use EventEmitter with C++ addons. I first tried to pass the EventEmitter.emit method directly to a C++ ObjectWrap constructor and invoke that method using Napi::Function::Call({args}) but the C++ code cannot see the actual EventEmitter object and thus fails since emit() accesses the EventEmitter class variables. This can be trivially solved by just wrapping the emit() call using a JS callback:

emitter.on("event", (...args) => {
    console.log("event occurred!");
    for (let [index, arg] of args.entries()) {
        console.log(`arg${index}: ${arg}`);
    }
});

function emit(event, ...args) {
    emitter.emit(event, args);
}

Foo = new Foo(emit);

And in C++:

Foo::Foo(Napi::CallbackInfo const & info) {
    this->emit = info[0].As<Napi::Function>();
    emit.Call({Napi::String::New(info.Env(), "event"), Napi::String::New(info.Env(), "arg"});
}

However, I am just wondering if this is the correct way to actually emit events or not.

@jasongin
Copy link
Member

jasongin commented Sep 3, 2017

but the C++ code cannot see the actual EventEmitter object and thus fails since emit() accesses the EventEmitter class variables

Sounds like you just need to bind the emit function to the emitter instance:

new Foo(emitter.emit.bind(emitter));

(And that is essentially what your adapter function does. But Function.bind() already exists for that purpose.)

@mhdawson
Copy link
Member

mhdawson commented Nov 2, 2017

#bennycooly did Jason's suggestion resolve your issue ? Just wondering if we can close this issue ?

@NickNaso
Copy link
Member

NickNaso commented Mar 26, 2018

Hi everyone,
today I worked at event emitter's usage with N-API. The approach proposed by @jasongin works. I created some simple examples here:
Use emitter from a function
Extend event emitter using object wrap
If anyone has time and want take a look and send me a feedback could be fantastic.

@mhdawson
Copy link
Member

mhdawson commented Apr 4, 2018

It might make sense to add those examples to: https://github.com/nodejs/abi-stable-node-addon-examples

@NickNaso
Copy link
Member

NickNaso commented Apr 4, 2018

Ok in this week I will work to complete another two examples about the event emitter usage after that I can add them to the repo.

NickNaso added a commit to NickNaso/abi-stable-node-addon-examples that referenced this issue Apr 16, 2018
NickNaso added a commit to NickNaso/abi-stable-node-addon-examples that referenced this issue Apr 16, 2018
NickNaso added a commit to NickNaso/abi-stable-node-addon-examples that referenced this issue Apr 16, 2018
Deleted bad examples


Added examples on how to use event emitter ref. nodejs/node-addon-api#110

Removed bad examples

Added examples for event emitter
See: nodejs/node-addon-api#110
@NickNaso
Copy link
Member

Hi @bennycooly did you solved the problem? May we close this issue?

mhdawson pushed a commit to nodejs/abi-stable-node-addon-examples that referenced this issue Apr 26, 2018
Porting asynchronous example from NAN repo.
The original example is hosted here:
https://github.com/nodejs/nan/tree/master/examples/async_pi_estimate.
Rewrite all the logic using **node-addon-api** See the following
issue https://github.com/nodejs/abi-stable-node-addon-examples/issues/10

Added examples for event emitter
See: nodejs/node-addon-api#110
mhdawson pushed a commit to nodejs/abi-stable-node-addon-examples that referenced this issue Apr 26, 2018
Porting asynchronous example from NAN repo.
The original example is hosted here:
https://github.com/nodejs/nan/tree/master/examples/async_pi_estimate.
Rewrite all the logic using **node-addon-api** See the following
issue https://github.com/nodejs/abi-stable-node-addon-examples/issues/10

Added examples for event emitter
See: nodejs/node-addon-api#110
@benfuu
Copy link
Author

benfuu commented Apr 28, 2018

Yes, we can close this. I got mine to work using the object wrap!

@benfuu benfuu closed this as completed Apr 28, 2018
@Globik
Copy link

Globik commented May 19, 2018

@NickNaso emit event from cpp is good but what about the same from c code?

@marcelman
Copy link

Hi @NickNaso,

I saw your example in https://github.com/NickNaso/addon-event-emitter/blob/master/00/src/emit-from-cpp.cc and it was very useful to understand how to use event emitter (I'm pretty new to NodeJS and NAPI). However, as you mention in your comment:

// All work but it's not a good practice bacause all long running task should be
// executed out of the event loop

Assuming that my worker thread is another C++ thread, how could I send the callback from that thread? for example:
emit.Call({Napi::String::New(env, "data"), Napi::String::New(env, "data ...")});

As I understand this is not supported by design in NodeJS, and I saw that the NAPI implementantion of (Napi::AsyncWorker) does not support a progress callback.

@NickNaso
Copy link
Member

NickNaso commented Aug 23, 2018

Hi @marcelman ,
you're right you cannot call JavaScript from the Napi::AsyncWorker in the specific you cannot call JavaScript from the Execute method.
For now this what you want to do is not directly supported by Napi::AsyncWorker, but you can take a look here: Asynchronous Thread-safe Function Calls that allow you to call JavaScript function from the additional thread created by the add-on when you use Napi::AsyncWorker.
Hope to be helpful and in the next days I try to create a new example that covers your needs.

@marcelman
Copy link

Hi @NickNaso,
Your answer was indeed helpful! thanks for pointing me in the right direction.

@zombiecong
Copy link

Hi @NickNaso ,
I also have same problem as @marcelman. Do you finish the new example?Is it possible to share it with me?

RaisinTen added a commit to RaisinTen/node-addon-examples that referenced this issue Jun 2, 2023
This was asked for in
nodejs/node-addon-api#110 (comment)
and
nodejs/node-addon-api#110 (comment).
The solution was described in text in
nodejs/node-addon-api#110 (comment)
but it would be better to have a code example that folks can refer to.

Signed-off-by: Darshan Sen <raisinten@gmail.com>
@RaisinTen
Copy link
Contributor

It's been a while but in case anyone else still needs the example, I've sent a PR to add it in - nodejs/node-addon-examples#211.

mhdawson pushed a commit to nodejs/node-addon-examples that referenced this issue Jun 28, 2023
This was asked for in
nodejs/node-addon-api#110 (comment)
and
nodejs/node-addon-api#110 (comment).
The solution was described in text in
nodejs/node-addon-api#110 (comment)
but it would be better to have a code example that folks can refer to.

Signed-off-by: Darshan Sen <raisinten@gmail.com>
@markpiffer
Copy link

markpiffer commented Feb 29, 2024

I was trying this with Napi::ThreadSafeFunction but it keeps crashing when Node encounters the function call in its event loop supposedly. The C++ itself runs through without problems.

Napi::ThreadSafeFunction emitFuncThreadSafe;

void configIAmEvent(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();

    Napi::Object eventEmitter = info[0].As<Napi::Object>();
    Napi::Value emitFuncValue = eventEmitter.Get("emit");
    if (emitFuncValue.IsFunction()) {
        Napi::Function emitFunction = emitFuncValue.As<Napi::Function>();
        emitFuncThreadSafe = Napi::ThreadSafeFunction::New(env, emitFunction, "IAmEventEmitter", 0, 5);
    }
}

void emit_iam(const char* ip) {
//    emitFuncThreadSafe.BlockingCall(ip, [](Napi::Env env, Napi::Function f, const char* ip) { f.Call({Napi::String::New(env,"IAm"), Napi::String::New(env,ip)}); });
      emitFuncThreadSafe.NonBlockingCall(ip, [](Napi::Env env, Napi::Function f, const char* ip) {
          f.Call({Napi::String::New(env, "IAm"), Napi::String::New(env, ip)}); });
}

The result is a:

#
# Fatal error in HandleScope::HandleScope
# Entering the V8 API without proper locking in place
#

Exception: Exception 0x80000003 encountered at address 0x7ff6138bc532
Exception: Exception 0x80000003 encountered at address 0x7ff6138bbde0

Process finished with exit code 3

I can assert that the (same) event was generated two times in the native code which I think is the reason for two Exceptions.

What am I doing wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants