如何编写一个在“返回”之前等待事件触发的 node.js 函数?

Posted

技术标签:

【中文标题】如何编写一个在“返回”之前等待事件触发的 node.js 函数?【英文标题】:How to write a node.js function that waits for an event to fire before 'returning'? 【发布时间】:2012-04-13 17:20:00 【问题描述】:

我有一个节点应用程序,它不是一个 Web 应用程序 - 它在返回 1 之前完成了一系列异步任务。在返回之前,程序的结果会立即打印到控制台。 p>

如何确保所有异步工作在返回前完成?通过确保我们在调用 res.end() 之前完成的所有任务,我能够在 Web 应用程序中实现与此类似的事情,但是在让脚本返回之前,我没有任何等效的方法可以调用最终的“事件”。

目前我的(损坏的)函数见下文,试图等到 callStack 为空。我刚刚发现这是一种荒谬的方法,因为 node 在进入 processObjWithRef 中调用的任何异步函数之前会等待 processHub 完成。

function processHub(hubFileContents)
    var callStack = [];
    var myNewObj = ;
    processObjWithRef(samplePayload, myNewObj, callStack);
    while(callStack.length>0)
        //do nothing
    
    return 1

注意:我之前曾多次尝试使用 async 之类的库来实现这种行为(请参阅我在 How can I make this call to request in nodejs synchronous? 处的相关问题),因此在建议任何基于“仅使用”的答案之前,请考虑那里的答案和 cmets异步”。

【问题讨论】:

"如何确保所有异步工作在返回前完成?"是一个无意义的问题 - 在所有异步任务完成之前,控制权永远不会返回到 shell,所以在最后一个异步函数完成之前保留控制台输出解决了我的问题。感谢大家提交答案。 【参考方案1】:

你不能在返回之前等待异步事件——这就是异步的定义!试图强迫 Node 进入这种编程风格只会让你痛苦。一个简单的例子是定期检查callstack 是否为空。

var callstack = [...];

function processHub(contents) 
  doSomethingAsync(..., callstack);


// check every second to see if callstack is empty
var interval = setInterval(function() 
  if (callstack.length == 0) 
    clearInterval(interval);
    doSomething()
  
, 1000);

相反,在 Node 中执行异步操作的常用方法是实现对函数的回调。

function processHub(hubFileContents, callback)
  var callStack = [];
  var myNewObj = ;
  processObjWithRef(samplePayload, myNewObj, callStack, function() 
    if (callStack.length == 0) 
      callback(some_results);
    
  );

如果您真的想退货,请查看promises;当它们被解决时,它们保证会立即或在将来的某个时间发出事件。

function processHub(hubFileContents)
  var callStack = [];
  var myNewObj = ;
  var promise = new Promise();

  // assuming processObjWithRef takes a callback
  processObjWithRef(samplePayload, myNewObj, callStack, function() 
    if (callStack.length == 0) 
      promise.resolve(some_results);
    
  );

  return promise;


processHubPromise = processHub(...);
processHubPromise.then(function(result) 
  // do something with 'result' when complete
);

【讨论】:

【参考方案2】:

问题在于您的功能设计。您希望从异步执行的任务列表中返回同步结果。

您应该使用一个额外的参数来实现您的函数,该参数将作为回调,您将在其中放置结果(在本例中为 1)以供某些消费者使用它。

另外你需要在你的内部函数中有一个回调参数,否则你将不知道它什么时候结束。如果这最后一件事是不可能的,那么您应该进行某种轮询(也许使用 setInterval)来测试 callStack 数组何时被填充。

请记住,在 javascript 中,您永远不应该忙着等待。这将完全锁定您的程序,因为它在单个进程上运行。

【讨论】:

投反对票,因为这不能回答问题。在某些情况下,您可能会编写一个小型命令行程序或 AWS Lambda,而您并不关心主线程是否被阻塞。【参考方案3】:

deasync 旨在准确解决您的问题。直接替换

while(callStack.length>0)
    //do nothing

require('deasync').loopWhile(function()return callStack.length>0;);

【讨论】:

【参考方案4】:

问题在于 node.js 是单线程的,这意味着如果一个函数运行,则在该函数返回之前没有其他函数运行(事件循环)。所以你不能阻止一个函数让它在异步完成后返回。

例如,您可以设置一个计数器变量来计算已启动的异步任务,并使用异步代码中的回调函数(在任务完成后调用)递减该计数器。

【讨论】:

【参考方案5】:

Node.js 在单线程事件循环上运行,并利用异步调用来执行各种操作,例如 I/O 操作。

如果您需要在执行其他代码之前等待多个异步操作完成 你可以尝试使用异步 -

Node.js Async Tutorial

【讨论】:

【参考方案6】:

您需要开始异步设计和思考,一开始可能需要一段时间才能习惯。这是一个简单的示例,说明如何在函数调用后处理诸如“返回”之类的问题。

function doStuff(param, cb) 
    //do something
    var newData = param;
    //"return"
    cb(newData);


doStuff(some:data, function(myNewData) 
    //you're done with doStuff in here
);

在 npm 上的 async 库中还有很多有用的实用功能。

【讨论】:

以上是关于如何编写一个在“返回”之前等待事件触发的 node.js 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用事件发射器调用返回值的函数,并在继续之前等待响应?

node.js 返回客户端等待事件

如何避免 Node js 中强大功能的提前返回

在返回位置之前如何等待计时器?

C#如何使同步方法等待事件触发?

如何让返回等待 MySQL 连接结束?节点.js