用Node-addon-api实现C++调用Javascript

本文介绍在使用Node-addon-api时,能够实现用C++调用Javascript的几种方式。

1. Callback

比较简单,示例:

  Napi::Function cb = info[0].As<Napi::Function>();
  cb.Call(env.Global(), { Napi::String::New(env, "hello world") });

Return function

String MyFunction(const CallbackInfo& info) {
  Env env = info.Env();
  return String::New(env, "hello world");
}

Function CreateFunction(const CallbackInfo& info) {
  Env env = info.Env();
  Function fn = Function::New(env, MyFunction, "funName");
  return fn;
}

2. 带有ThreadSafeFunction的callback(可以在C++中另写线程重复调用JS)

Napi::Value callback(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();

      if ( info.Length() < 2 )
      {
        throw Napi::TypeError::New( env, "Expected two arguments" );
      }
      else if ( !info[0].IsFunction() )
      {
        throw Napi::TypeError::New( env, "Expected first arg to be function" );
      }
      else if ( !info[1].IsNumber() )
      {
        throw Napi::TypeError::New( env, "Expected second arg to be number" );
      }

      int count = info[1].As<Napi::Number>().Int32Value();

      // Create a ThreadSafeFunction
      tsfn = Napi::ThreadSafeFunction::New(
          env,
          info[0].As<Napi::Function>(),  // JavaScript function called asynchronously
          "Resource Name",         // Name
          0,                       // Unlimited queue
          1,                       // Only one thread will use this initially
          []( Napi::Env ) {        // Finalizer used to clean threads up
            nativeThread.join();
          } );

      // Create a native thread
      nativeThread = std::thread( [count] {
        auto callback = []( Napi::Env env, Napi::Function jsCallback, int* value ) {
          // Transform native data into JS data, passing it to the provided
          // `jsCallback` -- the TSFN's JavaScript function.
          jsCallback.Call( {Napi::Number::New( env, *value )} );

          // We're finished with the data.
          delete value;
        };

        for ( int i = 0; i < count; i++ )
        {
          // Create new data
          int* value = new int( clock() );

          // Perform a blocking call
          napi_status status = tsfn.BlockingCall( value, callback );
          if ( status != napi_ok )
          {
            // Handle error
            break;
          }

          std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
        }

        // Release the thread-safe function
        tsfn.Release();
      } );

      return Napi::Boolean::New(env, true);

}

JS:

dlxPlugin.callback(() => {
    console.log("C++ call javascript function");
}, 2)

3. Event

Napi::Value event(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    Napi::Function emit = info[0].As<Napi::Function>();
    emit.Call({ Napi::String::New(env, "msg"), Napi::Number::New(env, 1) });
    emit.Call({ Napi::String::New(env, "msg"), Napi::Number::New(env, 10) });
}

JS:

const eventListener = new EventEmitter();

eventListener.on("msg", (data) => {
        console.log("Msg:", data);
    }
)
event(eventListener.emit.bind(eventListener));

4. 调用JS-eval,直接运行字符串

globalThis.num = 1;
console.log(nw.test());
console.log(globalThis.num); // 2
  Napi::String jsStr = Napi::String::New(env, "num++");
  napi_value result;
  napi_run_script(env, jsStr, &result);
  return Napi::Value::From(env, result);

可以这样运行函数

globalThis.xx = () => {
  return 233;
}
console.log(nw.test()); // 233
Napi::String jsStr = Napi::String::New(env, "xx()");

使用eval

globalThis.num = 1;
console.log(nw.test());
console.log(globalThis.num); // 2
  auto eval = env.Global().Get("eval").As<Napi::Function>();
  return eval.Call(env.Global(), {Napi::String::New(env, "num++")});