如何优雅的处理Nodejs中的异步回调

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何优雅的处理Nodejs中的异步回调相关的知识,希望对你有一定的参考价值。

Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用。在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O的操作结果基本上都需要在回调函数中处理,比如下面的这个读取文件内容的函数:

fs.readFile('/etc/passwd', function (err, data)  if (err) throw err;
console.log(data);
);

那,我们读取两个文件,将这两个文件的内容合并到一起处理怎么办呢?大多数接触js不久的人可能会这么干:

fs.readFile('/etc/passwd', function (err, data)  if (err) throw err;
fs.readFile('/etc/passwd2', function (err, data2)    if (err) throw err;    // 在这里处理data和data2的数据  );
);

那要是处理多个类似的场景,岂不是回调函数一层层的嵌套啊,这就是大家常说的回调金字塔或回调地狱(http://callbackhell.com/)的问题,也是让js小白最为头疼的问题。

这种层层嵌套的代码给开发带来了很多问题,主要体现在:

    代码可能性变差

    调试困难

    出现异常后难以排查

    本文主要是介绍如何优雅的处理以上异步回调问题。

    初级方案:通过递归处理异步回调

    我们可以使用递归作为代码的执行控制工具。把需要执行的操作封装到一个函数中,在回调函数中通过递归调用控制代码的执行流程,废话不多说,上个代码吧:

    var fs = require('fs');// 要处理的文件列表var files = ['file1', 'file2', 'file3'];function parseFile ()  if (files.length == 0)    return;
       var file = files.shift();
     fs.readFile(file, function (err, data)    // 这里处理文件数据
       parseFile();  // 处理完毕后,通过递归调用处理下一个文件  );
    // 开始处理parseFile();

    以上代码已依次处理数组中的文件为例,介绍了通过递归的方式控制代码的执行流程。

    应用到一些简单的场景中还是不错的,比如:我们将一个数组中的数据,依次保存到数据库中就可以采用这种方式。

    通过递归的方式可以解决一些简单的异步回调问题。不过对于处理复杂的异步回调还是显得有些无能为力(如需要同步多个异步操作的结果)。

参考技术A 不管是在Node中,还是其他的javascript中,只要使用异步就会进入回调陷阱。
之前的方法一般是利用第三方框架将异步写成同步方法,而框架在后台解释翻译将同步方法变回异步。
但是,当ES6出来后,多了Promise对象。可以利用Promise进行异步的链式传递。
在ES7出来后,Promise变得更简便了。可以利用Async/Await像写同步方法一样写异步方法。
如果需要获得ES6和ES7支持,请检查Node的版本及支持,可能需要Babel释义器等才能运行。本回答被提问者采纳

Nodejs 回调机制——哪个线程处理回调?

【中文标题】Nodejs 回调机制——哪个线程处理回调?【英文标题】:Nodejs callback mechanism - which thread handles the callback? 【发布时间】:2013-08-07 12:07:48 【问题描述】:

我是 nodeJS 的新手,想知道 Node.js 的单实例模型。 在一个简单的 nodeJs 应用程序中,当一些阻塞操作通过回调异步处理时,运行 nodeJs 的主线程是否也处理回调? 如果请求是从数据库中获取一些数据,并且有 100 个并发用户,并且每个 db 操作需要几秒钟,那么当最终触发回调时(对于每个连接),主线程是否接受这些请求也用于执行回调?如果是这样,nodeJs 是如何扩展的,它是如何快速响应的?

【问题讨论】:

发现这真的很有帮助:rickgaribay.net/archive/2012/01/28/… 【参考方案1】:

nodejs 的每个实例都在一个线程中运行。时期。当您对网络请求进行异步调用时,它不会等待它,也不会在您的代码或其他任何地方等待。它有一个贯穿的事件循环。当响应准备好时,它会调用您的回调。

这可以是令人难以置信的性能,因为它不需要大量的线程和所有的内存开销,但这意味着你需要小心不要做同步阻塞的东西。

http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/ 对事件循环有相当不错的解释,Ryan Dahl http://www.youtube.com/watch?v=ztspvPYybIY 的原始 jsconf 演示文稿值得一看。见过工程师因技术演示而起立鼓掌吗?

【讨论】:

这对我来说还是有点不清楚。因此,如果有 100 个并发用户,那么只有一个节点实例在运行还是 100 个?考虑到有一个带有回调的异步调用,并且线程(比如threadA)在发出network/db请求后退出了那段代码,当回调被触发时,是threadA执行了这个回调吗?。 是的。如果有 100 个“并发用户”(即用户对 nodejs 实例进行网络请求),那么仍然只有一个 node.js 实例。当发出请求时,单线程处理它。当它进行异步调用以读取文件或访问网络服务或其他任何内容时,它仍然是同一个线程。当访问完成并调用回调时,它仍然是同一个线程。您可以运行多个模式实例,前提是您可以在它们前面使用某种负载平衡器,并且节点集群已计划好但仍处于试验阶段。 @deitch 唯一的线程在同时做 100 件事情时如何不阻塞?比如说100件事情,每件事情需要1秒才能完成,如果全部都在1个线程中,它们必须串联完成,那么是不是意味着它会阻塞100秒? 问题是,在这 1 秒/用户中,是 1 秒 CPU 密集型处理,还是 10 毫秒处理和另外 990 毫秒等待网络、磁盘等?在大多数 Web 案例中,它是后者,而这正是 nodejs(和 nginx)最适合的地方。如果是前者,那么您将需要大量节点进程,或者转移到 Java/Scala 之类的东西(无论如何它仍然会占用相同数量的 CPU,只是有更多线程......) 当这一线程请求和I/O操作时,由操作系统处理。您的线程不需要等待响应,因此它只是继续移动并等待事件循环通知 I/O 请求的完成。

以上是关于如何优雅的处理Nodejs中的异步回调的主要内容,如果未能解决你的问题,请参考以下文章

nodejs异步回调函数中this问题,求助

如何使用 nodejs 将异步用于回调?

nodejs中的异步回调机制

nodejs如何从异步回调函数返回想要的值

nodejs知识结构

nodejs基础 -- 回调函数