【问题标题】:How to save an asynchronous callback for later using node-addon-api / napi如何使用 node-addon-api / napi 保存异步回调以供以后使用
【发布时间】:2020-03-13 10:40:45
【问题描述】:

我正在使用AsyncWorker 运行异步任务。问题是我有很多任务要一个接一个地运行,而且顺序很重要。为了保持秩序,我使用排队技术来确保AsyncWorkerobjects 是按照莱特顺序创建的,只有在每个任务完成后。我将回调存储在vector<Function> 中,并将其传递给AsyncWorker,但出现以下错误:

# Fatal error in v8::HandleScope::CreateHandle()
# Cannot create a handle without a HandleScope

还有其他方法可以解决这个问题吗?我也尝试过使用Napi::Persistent,但我无法将Napi::FunctionReference 变量传递给AsyncWorker

调用函数:

Napi::Value BlockChainWrapper::genesis(const Napi::CallbackInfo& info) {
    std::lock_guard<std::mutex> guard_ready_queue(ready_queue_mutex);
    this->ready_queue_callback.push_back(info[1].As<Napi::Function>());
    this->ready_queue_data.push_back(info[0].As<Napi::Object>());
    this->ready_queue_func.push_back(BlockChainWrapperTypes::_genesis_ready);
    this->ready_queue_env.push_back(info.Env());
    return info.Env().Undefined();
}
void BlockChainWrapper::genesis_ready() {
    AsyncBlockChainFunctions* asyncWorker = new AsyncBlockChainFunctions(this->ready_queue_callback.front(), 0, blockchain_obj, this->ready_queue_data.front());
    asyncWorker->Queue();
}

AsyncWorker 构造函数:

AsyncBlockChainFunctions::AsyncBlockChainFunctions(Napi::Function& callback, int mode, std::shared_ptr<BlockChain> _blockchain, Napi::Object& resource) : AsyncWorker(callback), mode(mode) {};

编辑 1 我实现了 PromiseWorker,但仍然遇到这些错误: BlockChainWrapper 继承 ObjectWrap。

Napi::Object BlockChainWrapper::Init(Napi::Env env, Napi::Object exports) {
    Napi::HandleScope scope(env);
    Napi::Function func = DefineClass(env, "BlockChainWrapper", {
        InstanceMethod("genesis", &BlockChainWrapper::genesis)
    });
    constructor = Napi::Persistent(func);
    constructor.SuppressDestruct();
    exports.Set("BlockChainWrapper", func);
    return exports;
}
# Fatal error in HandleScope::HandleScope
# Entering the V8 API without proper locking in place

修改了 AsyncWorker 的构造函数、类和解析函数:

class AsyncBlockChainFunctions : public PromiseWorker

AsyncBlockChainFunctions(Napi::Promise::Deferred const &d, std::shared_ptr<BlockChain> _blockchain, int mode, Napi::Object& resource) : PromiseWorker(d), mode(mode) {}

void Resolve(Napi::Promise::Deferred const &deferred) {
            deferred.Resolve(Napi::String::New(deferred.Env(), this->block_as_json_string));
};

调用函数:

Napi::Value BlockChainWrapper::genesis(const Napi::CallbackInfo& info) {
    std::lock_guard<std::mutex> guard_ready_queue(ready_queue_mutex);
    this->ready_queue_data.push_back(info[0].As<Napi::Object>());
    this->ready_queue_func.push_back(BlockChainWrapperTypes::_genesis_ready);
    this->ready_queue_env.push_back(info.Env());
    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
    std::cout << "genesis" << std::endl;
    return deferred.Promise();
}

从另一个队列管理线程调用Genesis ready

void BlockChainWrapper::genesis_ready() {
    Napi::Env env = ready_queue_env.front();
    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
    Napi::Object input_obj = this->ready_queue_data.front().As<Napi::Object>();
    auto *x = new AsyncBlockChainFunctions(std::ref(deferred), this->blockchain_obj, 0, input_obj);
    x->Queue();
}

【问题讨论】:

  • 你试过async.waterfall([])函数吗?
  • @SrikaraBS 是的,不工作
  • 你能告诉我你是怎么写异步瀑布的吗?我想看看
  • @SrikaraBS const testAddon = require('./build/Release/blockchainNativeApi.node'); const sha2 = new testAddon.BlockChainWrapper(); const async = require('async'); async.waterfall([ function() { console.log(sha2.genesis({ difficulty: 5, prev_hash: 'de04d58dc5ccc4b9671c3627fb8d626fe4a15810bc1fe3e724feea761965fb71', data: 'testing' }, AsyncWorkerCompletion)); } ], function AsyncWorkerCompletion (err, result) { if (err) { console.log(err); } else { console.log(result); } });

标签: c++ node.js node-addon-api


【解决方案1】:

我不确定我是否正确理解了您或能否帮助您解决最初的问题,但您不应依赖于AsyncWorker 的执行顺序。但是您提到的错误听起来就像您构造了AsyncWorker 错误,即CallbackInfo 可能有问题,即它所基于的napi 环境。

承诺

但是我强烈建议使用AsyncWorker 来处理我设计的生命周期,并切换到承诺模式。

基于https://github.com/nodejs/node-addon-api/issues/231#issuecomment-528986145

我建议您将其用作基类:

#include <napi.h>

class PromiseWorker : public Napi::AsyncWorker {
public:
    PromiseWorker(Napi::Promise::Deferred const &d) : AsyncWorker(get_fake_callback(d.Env()).Value()), deferred(d) {}

    virtual void Resolve(Napi::Promise::Deferred const &deferred) = 0;

    void OnOK() override {
        Resolve(deferred);
    }

    void OnError(Napi::Error const &error) override {
        deferred.Reject(error.Value());
    }

private:
    static Napi::Value noop(Napi::CallbackInfo const &info) {
        return info.Env().Undefined();
    }

    Napi::Reference<Napi::Function> const &get_fake_callback(Napi::Env const &env) {
        static Napi::Reference<Napi::Function> fake_callback
                = Napi::Reference<Napi::Function>::New(Napi::Function::New(env, noop), 1);
        fake_callback.SuppressDestruct();

        return fake_callback;
    }

    Napi::Promise::Deferred deferred;
};

然后你必须继承它,覆盖 ResolveExecute,并将你需要的东西保存在你的成员私有中,然后你就完成了。

更新:我做了一个关于如何使用这个承诺的完整工作示例:https://github.com/Superlokkus/spielwiese/tree/napi_promise_example

注意 Promise 方法:

#include <napi.h>

#include "promise_worker.hpp"

struct PromiseMethodWorker : PromiseWorker {
    PromiseMethodWorker(Napi::Promise::Deferred const &d, int input)
            : PromiseWorker(d), input_{std::move(input)} {}

    void Resolve(Napi::Promise::Deferred const &deferred) override {
        deferred.Resolve(create_js(deferred.Env()));
    }

    void Execute() override {
        output_ = input_ * 5;
    }

private:
    int input_;
    int output_;

    Napi::Number create_js(Napi::Env env) const {
        Napi::Number js_value = Napi::Number::New(env, output_);
        return js_value;
    }

};

Napi::Promise PromiseMethod(const Napi::CallbackInfo &info) {
    int input =  info[0].ToNumber();

    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());

    auto *wk = new PromiseMethodWorker(deferred, input);
    wk->Queue();

    return deferred.Promise();
}

使用及解决方法

所以你可以只使用 JS 中返回的承诺:

addon.PromiseMethod(42).then(value => add.PromiseMethod(value).then(...)) 

因此,您可以轻松地将这些 Promise 链接在一起,通过 Promise::all 等待所有。但是所以你避免so called callback hell。但同样,您的分散订单要求对我来说听起来像是 XY problem

因此,不是许多承诺/回调将其变为一个,因为AsyncWorker 似乎无法保证调用顺序。阻止一个承诺,可能会拖延一切。在您的本机代码中订购。

【讨论】:

  • 这样的? class PromiseWrapper : public PromiseWorker { public: PromiseWrapper(Napi::Promise::Deferred const &amp;d) : PromiseWorker(d) {}; void Execute() override { std::this_thread::sleep_for(std::chrono::seconds(3)); std::cout &lt;&lt; "slept for three seconds" &lt;&lt; std::endl; } void Resolve(Napi::Promise::Deferred const &amp;deferred) override { deferred.Resolve({}); } };
  • 是的,这行得通,我会写一个更像生活的例子,给我 5 分钟。
  • error C2440: '': 在编译期间无法从'initializer list' 转换为'T'
  • 谁说的?什么?我用 MSVC 编译了它,它工作得很好。我需要更多信息,请同时粘贴您的整个代码
  • 另外请注意我忘了提交我的分支,所以再次拉那个 git repo,yarn install &amp;&amp; yarn test 在 linux 和 windows 上都可以使用 VS2019
【解决方案2】:

以下内容甚至适用于数组

async.eachSeries(/*array*/, function(item, nextItem) {
    async.waterfall([
        function(callback) {
            //your logic
            callback(null, data);
        },
        function(data, callback) {
            //your logic
            callback(null, data2);  
        },
        function(data2, callback) {
            //your logic
            callback(null, 3); //The number is just for the argument purpose. Can be anything   
        }
        //You can continue adding as many steps as you want
    ],nextItem)         
});

【讨论】:

    猜你喜欢
    • 2019-06-15
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 2020-09-15
    • 2011-11-07
    相关资源
    最近更新 更多