对已解析的promise node.js响应缓慢

时间:2015-09-02 21:12:58

标签: node.js socket.io promise

我是node.js的新手,而且我觉得我做错了什么,但是我有更难的时间用Google搜索好的答案,然后我出于某种原因使用其他语言。基本上我使用节点包装的c ++插件来做一些工作然后我想在我做了一些额外的解析之后通过websocket发送它。当同步完成额外的解析时,它会立即发送(如预期的那样)。当我在一个promise(这是第三方模块正在为我做的事情)中完成工作后,在调用resolve之后,需要花费相当长的时间来调用已完成的回调。喜欢超过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进行多次回调。这些都是发布到主Event循环的事件,所以我不相信任何东西都阻塞了。 输出:

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....

2 个答案:

答案 0 :(得分:1)

.then()永远不会立即调用承诺resolve()回调。相反,它们总是异步调用。无论是同步调用还是异步调用,都可以提供统一的异步行为,从而使调用者的生活更轻松。

Promise等到当前执行线程中的其他代码完成执行,并且堆栈帧已经清除回“平台代码”,如规范所述。然后,调用.then()回调。如果你在那个执行线程中做了很多其他事情,那么在调用.then()处理程序之前可能会有一段延迟。

答案 1 :(得分:0)

我通过从原生v8 Promises切换到bluebird Promises来解决它。本机v8 Promise由一个Microtask队列处理,我不确定如何处理c ++插件(我永远无法获得一个好的堆栈跟踪),但看起来微任务队列变得饥饿,直到它最终在一些事件之后运行冲了它(仍然不确定)。 bluebird使用SetImmediate,它立即处理,为我解决了问题。

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

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