【问题标题】:Slow response to resolved promise node.js对已解决的承诺 node.js 响应缓慢
【发布时间】:2015-11-28 13:18:38
【问题描述】:

我是 node.js 的新手,我觉得我做错了什么,但由于某种原因,我很难在谷歌上搜索到好的答案,然后我会用其他语言搜索。基本上我正在使用一个节点包装的 c++ 插件来做一些工作,然后我想在对它进行一些额外的解析之后通过 websocket 发送它。当附加解析同步完成时,它会立即发送(如预期的那样)。当我在调用 resolve 后在 promise 中完成工作(这是第三方模块为我做的事情)时,调用已完成的回调需要很长时间。比如5-10秒以上。代码如下:

var addon = require('...'); // Node wrapped C++ module
...
var server = http.createServer(){...};
var ios = io.listen(server);

function someFunction(args) {
    console.log("Preparing to call some function");
    someOtherFunction(args).then(funtction(val) {
        console.log("Sending val over websocket....");
        ios.sockets.emit('thing', val);
    }
}

function someOtherFunction(args) {
    return new Promise(function(resolve, reject) {
        //logic
        resolve(someVal);
        console.log("Done with someOtherFunc");
    }
}

ios.sockets.on('connection', function(socket){
    console.log("Conneced");
    // This does some work on a different thread.  Eventually reposts to event loop and calls callback
    addon.doThing("someVal", someFunc);
    socket.on('disconnect', function() {
        console.log("Disconnected");
    });
});

输出:

Connected
Preparing to call some function...
Done with someOtherFunc;
<variable delay sometimes up to  5-10 seconds>
Sending val over websocket.

在第一次调用 someOtherFunction 完成之前,可能会发生对 someFunction 的多次回调。这些都是发布到主事件循环的事件,所以我不相信那里有任何阻塞。 输出:

Connected
Preparing to call some function...
Done with someOtherFunc;
<1 second delay>
Preparing to call some function...
Done with someOtherFunc;
<1 second delay>
Preparing to call some function...
Done with someOtherFunc;
<1 second delay>
<variable delay sometimes up to  5-10 seconds>
Sending val over websocket.
Sending val over websocket.
Sending val over websocket.

所以我的问题最初的想法是,当调用 resolve 时,立即调用回调。但这看起来并没有发生。某些东西正在触发回调以最终触发,但我不知道是什么,而且它实际上是随机的。任何帮助将不胜感激!

C++ 插件代码:

class NodeWrapper {
public:
  static NodeWrapper& GetInstance() {
    static NodeWrapper mInstance;
    return mInstance;
  }

  ~NodeWrapper() {
    uv_close((uv_handle_t*) &mNodeAsyncEventLoop, NULL);
  };

  static void SendToNode(uv_async_t *handle) {
    GetInstance().SendToNode();
  }

  void SendToNode() {
    uv_mutex_lock(&mMutex);
    swap(pushQueue, popQueue);
    uv_mutex_unlock(&mMutex);

    while(!popQueue.empty()) {
      Data* pData = popQueue.front();
      Isolate* isolate = Isolate::GetCurrent();
      HandleScope scope(isolate);
      const unsigned argc = 1;
      Local<Value> argv = { node::Buffer::New(isolate, pData->mData, pData->mLen, DataDeleter, NULL).ToLocalChecked() };
      Local<Function> cb = Local<Function>::New(isolate, mNodeCB);
      cb->Call(isolate->GetCurrentContext()->Global(), argc, &argv);
      popQueue.pop();
    }
    log << "Done with Callback!" << std::endl;
  }



  void SendOnData(const void* pData, DWORD dwLen) {
    log << "Callback received!" << std::endl;
    if(dwLen > 0) {
      uv_mutex_lock(&mMutex);
      pushQueue.push(new Data(pData, dwLen));
      uv_mutex_unlock(&mMutex);
      uv_async_send(&GetInstance().mNodeAsyncEventLoop);
    }
  };

  static NodeWrapper mInstance;
  UniquePersistent<Function> mNodeCB;

private:
  NodeWrapper() {
    uv_async_init(uv_default_loop(), &mNodeAsyncEventLoop, &NodeWrapper::SendToNode);
    uv_mutex_init(&mMutex);
  };

  DATA_QUEUE pushQueue;
  DATA_QUEUE popQueue;
  uv_mutex_t mMutex;
  uv_async_t mNodeAsyncEventLoop;
};



void doThing(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);
  auto f = std::bind(&NodeWrapper::SendOnData, &NodeWrapper::GetInstance());
  DoThingAnotherThreadAndCallCallback(f);
  NodeWrapper::GetInstance().mNodeCB = UniquePersistent<Function>(isolate, Local<Function>::Cast(args[1]));
}

输出更新:

22:01:57.831645 - Callback received

Preparing to call some function....
Done with someOtherFunc

22:01:57.839711 - Done with Callback!


22:01:57.918472 - Callback received

Preparing to call some function....
Done with someOtherFunc

22:01:57.927344 - Done with Callback!

Sending val over websocket....
Sending val over websocket....

【问题讨论】:

    标签: node.js socket.io promise


    【解决方案1】:

    Promise .then() 回调永远不会立即在 resolve() 上调用。相反,它们总是被异步调用。无论解析是同步调用还是异步调用,这都提供了统一的异步行为,从而使调用者的生活变得更加轻松。

    Promises 等到当前执行线程中的其他代码完成执行并且堆栈帧已清除回规范所说的仅“平台代码”。然后,调用.then() 回调。如果您在该执行线程中执行许多其他操作,那么在调用 .then() 处理程序之前可能会有延迟。

    【讨论】:

    • 堆栈帧已清除回仅“平台代码”是什么意思。我注意到在服务“then”回调之前,主循环响应来自我的 c++ 代码的多个回调。这些都是使用 libuv 中的 uv_async_send 发布的所有事件。这是否以比“then”回调更高的优先级运行?
    • @user1198542 - 可以这样想:setTimeout(fn, 1); 当前的代码线程完成执行,然后返回系统,事件队列中的下一个事件得到处理,该事件是调用.then() 处理程序。关键是在当前执行线程完成并返回系统之后才会调用它。
    • @user1198542 - 我无法真正回答您关于 C++ 代码的问题,因为我不知道该代码在做什么。 node.js 是一个事件驱动的系统。解决承诺会发布一个事件,该事件在轮到该事件运行时提供服务。我不知道的事件类型之间存在优先级层次结构。
    • 好的,我明白了。我感到困惑的是,事情似乎按以下顺序发生:C++Event fires -> Promise is Created -> Promise Resolved -> 2nd C++ event fires -> 2nd Promise is Created -> 2nd Promise issolved ->然后第一个 Promise 被回调 -> 2nd Promise 然后被回调。有什么好方法可以查看等待执行的事件以及主事件循环的顺序?
    • @user1198542 - 对于相同类型的事件(例如计时器事件),事件通常应按 FIFO 顺序进行服务,但如果通过 process.nextTick()、@987654328 完成某事的排序方式之间存在差异@ 和 setTimeout().
    【解决方案2】:

    我通过从原生 v8 Promises 切换到 bluebird Promises 解决了这个问题。本机 v8 Promises 由 Microtask 队列处理,我不确定 c++ 插件是如何处理的(我永远无法看到好的堆栈跟踪),但看起来 microtask 队列正在挨饿,直到它最终在某些事件必须运行后运行冲洗它(仍然不确定)。 bluebird 使用 SetImmediate 并立即对其进行处理,这为我解决了问题。

    在此处阅读进一步阅读:https://github.com/nodejs/node-v0.x-archive/issues/7714

    这在 v0.12 中已“修复”,我实际上使用的是 io.js v3.2.0。我不是 100% 确定这是同一个问题还是不同的问题。无论哪种方式,我现在都会坚持使用蓝鸟

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-02
      • 1970-01-01
      • 1970-01-01
      • 2021-05-25
      • 1970-01-01
      • 2017-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多