【问题标题】:How to check in an node.js C++ addon, if a promise is resolved如果 promise 已解决,如何签入 node.js C++ 插件
【发布时间】:2018-06-28 10:54:22
【问题描述】:

任务:

我从 C++ 调用一个回调,它返回一个 v8::Promise(所以是一个异步函数)。 现在我想知道 promise 是否已解决。

对于这里的示例,我想从 JS 中检查承诺是否已解决。 但是“只是在 C++ 插件中得到通知”就可以了。

问题:

我未能在 C++ 中创建持久 Promise 对象。 我还在事件循环中工作。但是当我稍后再次进入事件循环时,对象是空的。

代码:

JS测试代码

// create an object in the addon
var OB = require('./build/Debug/objectwraphandle.node')
var obj = new OB.MyObject(42)

// just an async wait function
async function asyncFunc1(y) {
    return new Promise((resolve, reject) => { 
        setTimeout(() => resolve('asyncFunc1: DONE'), 2000);
    });
}

// pass the async function as callback to the addon and call it
obj.callAsyncFunction(asyncFunc1);

//  ask the object, it the promise is already resolved.
// this should switch from 1 (Pending) to 0 (Resolved) after 2 seconds
console.log("  resolved? : " + obj.isPromiseResolved());
// but this core-dumps with:
// "FATAL ERROR: v8::Promise::Cast Could not convert to promise"

现在是 C++ 方面(顺便说一下 - 不用说我既不是 C++ 也不是 JS 大师) (原文取自 NAN - Node.js 的本机抽象)。 为了复制,我把完整的代码放在这里。重要的是函数 CallAsyncFunctionIsPromiseResolved

/*********************************************************************
 * NAN - Native Abstractions for Node.js
 * Copyright (c) 2018 NAN contributors
 * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
 *
 * and now here:  used as test basis by Gerrit Prange
 ********************************************************************/

#include <iostream>
#include <nan.h>

using namespace Nan; 

class MyObject : public ObjectWrap {
 public:
  static NAN_MODULE_INIT(Init) {
    v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
    tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    // these are the two functions in question
    SetPrototypeMethod(tpl, "callAsyncFunction", CallAsyncFunction);
    SetPrototypeMethod(tpl, "isPromiseResolved", IsPromiseResolved);

    constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
    Set(target, Nan::New("MyObject").ToLocalChecked(),
      Nan::GetFunction(tpl).ToLocalChecked());
  }

 private:
  explicit MyObject(double value = 0) : value_(value) {}
  ~MyObject() {}

  // here: the promise is stored as persistent object
  Nan::Persistent<v8::Promise> *persistentPromise;

  static NAN_METHOD(New) {
    if (info.IsConstructCall()) {
      double value = info[0]->IsUndefined() ? 0 : Nan::To<double>(info[0]).FromJust();
      MyObject *obj = new MyObject(value);
      obj->Wrap(info.This());
      info.GetReturnValue().Set(info.This());
    } else {
      const int argc = 1;
      v8::Local<v8::Value> argv[argc] = {info[0]};
      v8::Local<v8::Function> cons = Nan::New(constructor());
      info.GetReturnValue().Set(
          Nan::NewInstance(cons, argc, argv).ToLocalChecked());
    }
  }

  /* we get a callback function (async function),
   * call this callback and get a promise returned
   */
  static NAN_METHOD(CallAsyncFunction) {
    MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());

    const unsigned argc = 1;
    v8::Local<v8::Value> argv[argc] = { Nan::New("hello world").ToLocalChecked() };

    Callback cb(To<v8::Function>(info[0]).ToLocalChecked());

    // call the callback - and get a Promise
    Nan::MaybeLocal<v8::Value> promiseReturnValue = (*cb)->Call(GetCurrentContext()->Global(), argc, argv);

    // check if the promise is already resolved. (should not be in this example!)
    v8::Handle<v8::Promise> promiseReturnObject = v8::Handle<v8::Promise>::Cast ( promiseReturnValue.ToLocalChecked() );
    v8::Promise::PromiseState promiseState = promiseReturnObject->State();
    std::cout <<  " state: " << promiseState << std::endl;

    // make the callback persistent
    Nan::Persistent<v8::Promise> persistentPromiseReturnObject(promiseReturnObject);
    obj->persistentPromise = &persistentPromiseReturnObject;
  }

  /* check if the callback is already resolved and return the state
   */
  static NAN_METHOD(IsPromiseResolved) {
    MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());

    v8::Local<v8::Context> context = v8::Isolate::GetCurrent()->GetCurrentContext();

    // get the persistent callback and convert it into a local object
    v8::Local<v8::Object> objectToCheckPromise = Nan::New ( *obj->persistentPromise );
    // THE LINE BELOW IS THE PROBLEM! actually, persiting does not seem to work.
    v8::Local<v8::Promise> promiseObject = v8::Local<v8::Promise>::Cast ( objectToCheckPromise );

    // get the promises state
    v8::Promise::PromiseState promiseState = promiseObject->State();

    // and return the state
    std::cout <<  " in IsPromiseResolved state: " << promiseState << std::endl;
    info.GetReturnValue().Set(Nan::New(promiseState));
  }

  static inline Persistent<v8::Function> & constructor() {
    static Persistent<v8::Function> my_constructor;
    return my_constructor;
  }

  double value_;
};

NODE_MODULE(objectwraphandle, MyObject::Init)

我得到的实际错误是:

FATAL ERROR: v8::Promise::Cast Could not convert to promise
 1: node::Abort() [node]
 2: 0x121a2cc [node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: v8::Promise::Cast(v8::Value*) [/home/gpr/projects/own/nodejs/jsFromC_NAN_PromiseWait/build/Debug/objectwraphandle.node]
 5: v8::Local<v8::Promise> v8::Local<v8::Promise>::Cast<v8::Object>(v8::Local<v8::Object>) [/home/gpr/projects/own/nodejs/jsFromC_NAN_PromiseWait/build/Debug/objectwraphandle.node]
 6: MyObject::IsPromiseResolved(Nan::FunctionCallbackInfo<v8::Value> const&) [/home/gpr/projects/own/nodejs/jsFromC_NAN_PromiseWait/build/Debug/objectwraphandle.node]
 7: 0x7fb4dd8889dc [/home/gpr/projects/own/nodejs/jsFromC_NAN_PromiseWait/build/Debug/objectwraphandle.node]
 8: v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) [node]
 9: 0xb9043c [node]
10: v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [node]

抱歉,这篇文章很长。 (但我想把代码放在这里——如果有人想试试的话。)

【问题讨论】:

    标签: node.js v8 node.js-addon node-nan


    【解决方案1】:

    您可以将 Promise 存储在持久句柄中。您的代码应类似于:

    class MyObject : public ObjectWrap {
      // ...
    private:
      Nan::Persistent<Promise> persistentPromise; // not a pointer!
    }
    
    NAN_METHOD(CallAsyncFunction) {
      MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());
      // ... your function call here
      // returnobj is a Local<Object>
      Local<Promise> p = Local<Promise>::Cast(returnobj);
      obj->persistentPromise.Reset(p);
    }
    
    NAN_METHOD(IsPromiseResolved) {
      MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.This());
      Local<Promise> p = obj->persistentPromise.Get(Isolate::GetCurrent());
      info.GetReturnValue().Set(p->State());
    }
    

    我不知道是否有正当理由将 v8 类型的地址或取消引用作为嵌入器(查看您的回调调用以及您如何设置持久句柄)。有一些 API 方法可以安全地执行这些操作。

    顺便说一句,Handle 已弃用; Local 是它的替代品。

    【讨论】:

    • 您能帮忙添加回调以查看承诺何时解决吗?这将让我定期检查它是否已解决,但如果我只是添加 .Then( context, Local .. ) 我应该能够在它解决时获得这些回调。我当前的代码抛出错误'TypeError: Method Promise.prototype.then called on incompatible receiver undefined'
    【解决方案2】:

    好的,看起来 v8 Promise 不是一个可以持久化的对象。

    但你可以这样做:

    向 Promise 添加 resolve 和 reject 回调,以便您在 Promise 被解决/拒绝时得到通知。 (我本来想添加“直接 c++ 回调”而不是通过 JS,但现在没关系。)

    或者您在您的代码中创建一个解析器,它可以持久化,您可以稍后在您的代码中解析或拒绝您的解析器。 (这对我来说是解决问题的另一种方法——也许朝那个方向看也对你有帮助?)

    【讨论】:

      猜你喜欢
      • 2020-11-26
      • 2020-11-03
      • 1970-01-01
      • 2021-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多