前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >有效使用 Node.js 事件循环

有效使用 Node.js 事件循环

作者头像
疯狂的技术宅
发布2019-03-28 14:35:28
1.6K0
发布2019-03-28 14:35:28
举报
文章被收录于专栏:京程一灯京程一灯京程一灯

对于 Node.js 应用程序开发新手而言,作为学习曲线的一部分,他们需要了解单线程事件循环的工作原理,以及它可能导致意外结果的方式。您可以使用本教程中的 3 个交互式示例中的事件循环进行练习。您很快就能编写快速、高效的代码来轻松处理异步调用。

我们将通过 3 段简单的代码段来演示事件循环的工作原理。

示例 1:一个简单示例

第一个示例定义了 3 个函数并调用了它们。单运行该代码。然后尝试更改 setTimeout() 调用中的数字值,以查看输出有何变化。例如,将所有值都设置为 0。

function printEventually(message) {

setTimeout(function () {console.log(message);}, 200);

}

function printSoon(message) {

setTimeout(function () {console.log(message);}, 100);

}

function printNow(message) {

console.log(message);

}

printEventually('world!');

printNow('Hello');

printSoon('there,');

您可能期望初始示例生成以下输出:

world!

Hello

there,

但是,实际的输出并不是这样的。printNow('Hello') 最先执行,然后是 printSoon('there,'),最后是 printEventually('world!')。这是因为当 Node 引擎在 printEventually() 调用中看到 setTimeout() 时,会将它转交给操作系统,然后继续调用 printNow()。该代码是同步的,所以消息 Hello 会立即打印出来。最后,对 printSoon() 中的 setTimeout() 的调用被转交给操作系统。一秒后(printSoon() 等待 1000 毫秒),printSoon() 中的 setTimeout() 计时器过期,因此会将消息 there, 打印到控制台。再过一秒后,最后一个 setTimeout() 过期,world! 显示出来。因此,3 个语句按以下顺序处理:

Hello

there,

world!

事件循环的工作原理

传统 Web 服务器是多线程的,每个会话通常都有自己的线程。该方法很有效,但当会话空闲时,它会要求 Web 服务器分配未被使用的资源。这些空闲会话的开销,使得扩展服务器来处理需求峰值变得更加困难。

另一方面,Node 引擎包含一个线程,用于应对操作系统发出的所有事件处理通知。如果该操作是异步的(例如,调用数据库或 REST 接口),Node 引擎会要求操作系统在准备好处理调用时通知它(比如在数据从数据库或 REST 调用传来时)。在此期间,Node 事件循环会前进到需要执行的下一个操作。

您需要了解,Node 引擎会立即处理每个操作。在一些情况下,“立即” 意味着要求操作系统在某个操作准备好处理时获知此事。

示例 2:回调模式

尽管第一个示例演示了 Node 如何处理异步代码,但您通常会采用回调模式 来调用异步代码。该模式如下所示:

清单 1. 定义异步函数的伪代码

function asyncCode(arg1, arg2, callback) {

// Do whatever the function does here. When it's complete, it should

// invoke the callback function, returning a (hopefully null)

// JavaScript Error object and the results of this function.

return callback(error, results);

}

传递给 asyncCode() 的最后一个参数是另一个函数。当 asyncCode() 完成其工作时,它会调用传递给它的回调函数。根据惯例,异步函数会将一个 JavaScript Error 对象作为第一个参数传递给回调,然后传递异步函数生成的结果。请注意,asyncCode() 函数可以拥有它所需要的任意多个参数,而且它可以将任意多个必要参数传递给回调函数。

这就是定义异步函数的方式。下面给出了调用异步函数的代码:

清单 2. 调用异步函数的伪代码

asyncCode(x, 37, function(error, results) {

if (error != null)

// Handle the error if there is one

else

// Otherwise do whatever we want with the results

});

这个代码版本使用了回调函数。按原样运行该代码。然后尝试更改 printMessage() 调用中的数字值,以查看输入有何变化。尝试将 console.log('Hello') 替换为对 printMessage() 的另一次调用。如果 timeout 值为 0,会发生什么?这是否会影响执行顺序?

function printMessage(timeout, message, callback) {

var error;

setTimeout(function () {return callback(error, message);}, timeout);

}

printMessage(200, 'world!', function(error, message) {

if (error != null)

console.error('Something went wrong!');

else

console.log(message);

});

console.log('Hello');

printMessage(100, 'there,', function(error, message) {

if (error != null)

console.error('Something went wrong!');

else

console.log(message);

});

printMessage() 函数将会实现回调模式。它设置了一个超时,因此 Node 会将该超时传递给操作系统。然后,Node 继续执行下一个操作。在本例中,下一个操作是对 console.log() 的一次简单调用。然后是对 printMessage() 的另一次调用,这次调用会设置另一个超时。超时过期时代码结束运行,并将 there, 和 world! 写入到控制台。回调函数生成了与第一个示例相同的消息:

Hello

there,

world!

示例 3:嵌套回调

如果出于某种原因,您想要按特定顺序打印消息中的 3 个单词,则需要嵌套这些回调函数。例如,如果 timeout 参数是 0 和 5000 之间随机生成的数字,那么您就无法知道将获得什么消息。

按原样运行该代码。现在尝试更改 printMessage() 调用中的数字值。无论您使用什么值,该代码都会按相同顺序执行。

function printMessage(timeout, message, callback) {

var error;

setTimeout(function () {return callback(error, message);}, timeout);

}

printMessage(200, 'world!', function(error, message) {

console.log(message);

printMessage(0, 'Hello', function(error, message) {

console.log(message);

printMessage(100, 'there,', function(error, message) {

console.log(message);

});

});

});

此代码确保对 printMessage() 的这 3 次调用是按特定顺序进行的。对 printMessage() 的第一次调用传入了一个也称为 printMessage() 的回调函数,该回调函数随后传入了另一个称为 printMessage() 的回调函数。该代码生成以下混乱的问候语:

world!

Hello

there,

该代码相对容易理解,因为我们忽略了错误处理,在再次调用 printMessage() 前只有一行代码。如果将错误处理添加回代码中,并在调用之间形成复杂的逻辑,这很快就会造成回调噩梦,导致代码嵌套多层且难以理解。

结束语

我们快速查看了如何使用 Node.js 单线程事件循环。使用 Node 库来访问数据库和文件等对象时,了解如何处理异步方法 — 和如何确保代码按一定的顺序执行 — 是至关重要的技能。随着对事件循环的深入理解,您就能编写快速、高效的代码来轻松处理异步调用。


小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京程一灯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档