• UID19083
  • 登录2017-12-07
  • 粉丝0
  • 关注0
  • 发帖11
  • 主页
  • 金币52枚
qq_rFRhEoj14991 发布于2017-08-14 13:40
0/169

Node.js 事件循环的有效使用

楼层直达
Node全栈爱好者的交流池~入 550392000,分享更多Node.js技术知识~
对于 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() 前只有一行代码。如果将错误处理添加回代码中,并在调用之间形成复杂的逻辑,这很快就会造成回调噩梦,导致代码嵌套多层且难以理解。



本文原作者:京程一灯

0人打赏
您需要登录后才可以回帖
发表回复
极贡献
技术问答
专题荟萃
程序人生
视觉设计
Android开发
iOS开发
编程语言
前端开发
后端开发
服务器架构
软件测试
运维方案
创业路上



最热文章墙

  • 80581/379   【精品推荐】200多种Android动画效果的强悍框架,太全了,不看这个,再有动画的问题,不理你了^@^

  • 45809/191   情人节福利,程序员表白的正确姿势:改几行代码就变成自己的表白了

  • 45498/0   Python爬虫:常用浏览器的useragent

  • 42016/261   【精品推荐】Android版产品级的音乐播放器源码,功能太强大了,最好的产品原型有木有?

  • 39011/145   省时省力的Android组件群来了,非常棒的原型参考

  • 30465/143   2016抢红包软件及源码

  • 29524/71   原创表白APP,以程序员的姿势备战新年后的7夕,持续完善中!

  • 29452/2   超全!整理常用的iOS第三方资源

  • 24784/161   Android版类似UC浏览器:非常赞,产品级的源码

  • 23028/31   麻省理工的一帮疯子,真的实现了随意操控万物!(绝对黑科技)

  • 22730/26   Android工程师面试题大全

  • 22689/27   2016程序员跳槽全攻略

  • 22061/10   GitHub上排名前50的iOS项目:总有一款你用得着

  • 21063/21   码魂:程序员的牛B漫画

  • 19280/74   【持续更新中】Android福利贴(二):资料源码大放送

  • 19279/85   Android小而全的博客源码:非常适合全面掌握开发技巧

  • 19145/43   一个绚丽的loading动效分析与实现!

  • 19102/10   2016年最全的Android面试考题+答案 精编版

  • 18864/3   吐槽那些程序员的搞笑牛逼注释

  • 17848/45   惊艳的App引导页:背景图片切换加各个页面动画效果

  • 17748/104   Android带弹幕的视频播放器源码,来自大名鼎鼎的Bilibili弹幕网站

  • 17731/1   iOS 动画总结

  • 17709/82   仿京东商城客户端Android最新版,不错的原型和学习资料

  • 17363/25   个人收集的Android 各类功能源代码

  • 16888/5   新一代Android渠道打包工具:1000个渠道包只需要5秒

  • 16845/22   Android福利第三波【Android电子书】

  • 16652/1   iOS中文版资源库,非常全

  • 16648/81   【精品推荐】类似360安全卫士安Android源码:非常赞的产品原型

  • 16614/54   基于瀑布流的美女图片浏览App,有注释的源代码

  • 16488/18   用JavaScript 来开发iOS和Android 原生应用:React Native开源框架中文版来啦

  • 16482/10   女程序员的梦,众网友的神回复

  • 16187/11   年会上现场review代码是怎么样的体验!

  • 16185/23   珍藏多年的素材,灵感搜寻网站

  • 15791/19   65条最常用正则表达式,你要的都在这里了

  • 14980/16   基于Android支付宝支付设计和开发方案

  • 14391/11   有木有这样一张酷图帮你集齐所有git命令超实用

  • 14312/18   什么是真正的黑客:收获12200+Stars,人气远超微软开源VS

  • 14130/47   在线音乐播放器完整版(商用级的源码):非常赞,可听免费高品质专辑

  • 13929/62   【技巧一】搭配Android Studio,如何实现App远程真机debug?

  • 13814/0   GitHub iOS 库和框架Top100 

  • 13656/7   用程序员的姿势抢过年的火车票

  • 13625/7   一张图搞定iOS学习路线,非常全面

  • 13260/10   成为Java顶尖程序员 ,看这11本书就够了

  • 13219/10   微信支付终于成功了(安卓,iOS),在此分享

  • 13102/18   一张图搞定Android学习路线,非常全面

  • 12954/29   【持续更新中】Android福利贴(一):资料源码

  • 12857/4   基于Node.js的强大爬虫,能直接发布抓取的文章哦

  • 12661/4   46 个非常有用的 PHP 代码片段

  • 12129/3   即时通信第三方库

  • 11556/9   流媒体视频直播方案

  • 11512/18   八个最优秀的Android Studio插件

  • 11364/9   B站建开源工作组:APP想支持炫酷弹幕的看过来

  • 11191/2   【精品推荐】高质量PHP代码的50个实用技巧:非常值得收藏

  • 11136/9   烧了5亿美金,这家神秘的公司即将颠覆人类未来!

  • 11063/12   中国黑客的隐秘江湖:攻守对立,顶尖高手月入千万美元

  • 10495/1   基于node-webkit跨平台应用案例集之(一)

  • 10315/6   开箱即用!Android四款系统架构工具

  • 10113/11   十大技巧快速提升Android应用开发性能

  • 10098/4   10款GitHub上最火爆的国产开源项目——可以媲美西半球

  • 10055/2   Android性能优化视频,文档以及工具

  • 返回顶部