如何为 Node.js 编写异步函数

Posted

技术标签:

【中文标题】如何为 Node.js 编写异步函数【英文标题】:How to write asynchronous functions for Node.js 【发布时间】:2011-10-17 10:43:01 【问题描述】:

我试图研究应该如何编写异步函数。在翻阅了大量文档之后,我仍然不清楚。

如何为 Node 编写异步函数?我应该如何正确实现错误事件处理?

问我问题的另一种方式是:我应该如何解释以下函数?

var async_function = function(val, callback)
    process.nextTick(function()
        callback(val);
    );
;

另外,我发现 this question on SO(“如何在 node.js 中创建非阻塞异步函数?”)很有趣。我觉得还没有回答。

【问题讨论】:

这就是我问的原因。我看不出这些功能有何不同。 我建议您在自己喜欢的浏览器中查看setTimeoutsetInterval 并与它们一起玩。或者 ajax 回调(可能是最接近节点体验的东西),或者你熟悉的事件监听器,比如点击和加载事件。异步模型在浏览器中已经存在,在node中完全一样。 @davin - 那时我猜我还没有完全理解异步模型。 @Kriem,我昨天回答了一些可能有帮助的问题:***.com/questions/6883648/… 这不是你问题的答案,但它是主题。尝试阅读那里的问题和答案,并尝试使用代码来尝试了解发生了什么。 @Raynos “异步函数”的定义是什么? 【参考方案1】:

您似乎将异步 IO 与异步函数混淆了。 node.js 使用异步非阻塞 IO,因为非阻塞 IO 更好。了解它的最好方法是去看 ryan dahl 的一些视频。

如何为 Node 编写异步函数?

只写普通函数,唯一的区别是它们不是立即执行而是作为回调传递。

我应该如何正确实现错误事件处理

通常,API 会为您提供一个回调,并将错误作为第一个参数。例如

database.query('something', function(err, result) 
  if (err) handle(err);
  doSomething(result);
);

是一种常见的模式。

另一个常见的模式是on('error')。例如

process.on('uncaughtException', function (err) 
  console.log('Caught exception: ' + err);
);

编辑:

var async_function = function(val, callback)
    process.nextTick(function()
        callback(val);
    );
;

上述函数调用时

async_function(42, function(val) 
  console.log(val)
);
console.log(43);

将异步打印42 到控制台。特别是 process.nextTick 在当前事件循环调用堆栈为空后触发。在 async_functionconsole.log(43) 运行后,该调用堆栈为空。所以我们打印 43,然后打印 42。

您可能应该阅读一下事件循环。

【讨论】:

我看过 Dahl 的视频,但恐怕我并没有掌握这件事。 :( @Kriem 查看更新的答案并阅读about the event loop 感谢您的见解。我现在更加意识到我缺乏知识。 :) 顺便说一句,您的最后一个示例有所帮助。 我认为您关于异步 IO 的说法“更好”太笼统了。从这个意义上说是的,但总体而言可能并非如此。 在您的第一个代码示例中,您检查了 err 参数,但之后没有返回。如果出现错误,代码将继续运行,并可能导致您的应用程序出现严重问题。【参考方案2】:

仅仅通过回调是不够的。 例如,您必须使用 settimer 来使函数异步。

示例: 不是异步函数:

function a() 
  var a = 0;    
  for(i=0; i<10000000; i++) 
    a++;
  ;
  b();
;

function b() 
  var a = 0;    
  for(i=0; i<10000000; i++) 
    a++;
  ;    
  c();
;

function c() 
  for(i=0; i<10000000; i++) 
  ;
  console.log("async finished!");
;

a();
console.log("This should be good");

如果你要运行上面的例子,这应该很好,必须等到这些函数完成才能工作。

伪多线程(异步)函数:

function a() 
  setTimeout ( function() 
    var a = 0;  
    for(i=0; i<10000000; i++) 
      a++;
    ;
    b();
  , 0);
;

function b() 
  setTimeout ( function() 
    var a = 0;  
    for(i=0; i<10000000; i++) 
      a++;
    ;  
    c();
  , 0);
;

function c() 
  setTimeout ( function() 
    for(i=0; i<10000000; i++) 
    ;
    console.log("async finished!");
  , 0);
;

a();
console.log("This should be good");

这将是真正的异步。 这应该会在异步完成之前写好。

【讨论】:

【参考方案3】:

你应该看这个:Node Tuts episode 19 - Asynchronous Iteration Patterns

它应该回答你的问题。

【讨论】:

【参考方案4】:

如果你知道一个函数返回一个 promise,我建议使用 javascript 中新的 async/await 特性。它使语法看起来同步但异步工作。当您将 async 关键字添加到函数时,它允许您在该范围内 await 承诺:

async function ace() 
  var r = await new Promise((resolve, reject) => 
    resolve(true)
  );

  console.log(r); // true

如果一个函数没有返回一个promise,我建议将它包装在一个你定义的新promise中,然后解析你想要的数据:

function ajax_call(url, method) 
  return new Promise((resolve, reject) => 
    fetch(url,  method )
    .then(resp => resp.json())
    .then(json =>  resolve(json); )
  );


async function your_function() 
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); //  status: 200, data: ... 

底线:利用 Promises 的力量。

【讨论】:

这里要记住的是,promise 的主体仍然是同步执行的。【参考方案5】:

试试这个,它适用于节点和浏览器。

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) 
  process.nextTick(function () 
    func();
  );
 : function (func) 
  setTimeout(func, 5);
);

【讨论】:

4 票反对,甚至没有一条建设性评论.. :\ @Omer 这就是 SO 上的生活。 @NorbertoBezi 也许代码对您来说是不言自明的,但对于发布答案的人来说却不是。这就是为什么在投反对票时解释总是一个好习惯的原因。【参考方案6】:

我已经为 node.js 中的此类任务处理了太多时间。我主要是前端的人。

我觉得这点很重要,因为所有节点方法都是异步处理回调的,把它转换成 Promise 处理比较好。

我只是想展示一个可能的结果,更精简和可读。将 ECMA-6 与 async 一起使用,您可以这样编写。

 async function getNameFiles (dirname) 
  return new Promise((resolve, reject) => 
    fs.readdir(dirname, (err, filenames) => 
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    )
  )

(undefined || null) 用于 repl(读取事件打印循环)场景, 使用 undefined 也可以。

【讨论】:

以上是关于如何为 Node.js 编写异步函数的主要内容,如果未能解决你的问题,请参考以下文章

当循环中调用了异步函数时,Node.JS 将如何处理循环控制?

如何为特定版本的 node.js 安装 d.ts?

如何为 node.js 服务器分配域名? [复制]

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

如何为特定令牌、颤振、firebase 消息传递和 node.js 发送通知

如何为 Node JS 设置 REST API