由于JavaScript是单线程的,那么在浏览器中,为了在等待动作完成时不会阻塞主线程的异步代码处理,JavaScript使用事件循环在调用堆栈、Web API和回调队列之间,持续协调代码的执行。不过,由Node.js自行实现的Node.js事件循环,虽然与之有着许多相同的模式,但是由于Node.js不与DOM交互,且可以处理各种输入和输出(I/O),因此它在工作方式上却有所不同。
“只有客户发展了,才有我们的生存与发展!”这是创新互联公司的服务宗旨!把网站当作互联网产品,产品思维更注重全局思维、需求分析和迭代思维,在网站建设中就是为了建设一个不仅审美在线,而且实用性极高的网站。创新互联对成都网站设计、成都网站制作、网站制作、网站开发、网页设计、网站优化、网络推广、探索永无止境。
在本文中,我们将先了解Node.js事件循环背后的理论,再探究几个使用setTimeout、setImmediate和process.nextTick的示例。最后,我们将部分工作代码部署到Heroku(这一种快速部署应用的简便方法,请参阅--https://www.heroku.com/)中,以查看其运行情况。
总的说来,Node.js事件循环可以协调计时器、回调、以及I/O事件等操作与执行。这便是Node.js在单线程的情况下,处理异步行为的方式。如下事件循环图,很好地展示了其执行的顺序。
如您所见,Node.js事件循环共有六个主要阶段,它们分别是:
您可能会好奇,为何process.nextTick并未在上述任何阶段被提到?其实,这是因为:作为一种特殊的方法,就技术而言,它并非Node.js事件循环的一部分。相反,无论process.nextTick方法在何时被调用,它都会将自己的回调放入队列之中,然后“无论事件循环当前处于哪个阶段,都会在完成当前操作后,处理排队中的各种回调”(源自:Node.js事件循环文档)。
也许您觉得上文针对Node.js事件循环的每个阶段的解释,过于抽象了。那么,我在Heroku上创建了一个包含了各种可运行代码段示例的演示应用,请参见--https://nodejs-event-loop-demo.herokuapp.com/。在该应用中,单击任何示例按钮,都会向服务器端发送一个API请求。而Node.js会在后端执行所选示例的代码片段,然后通过API将相应的响应返回给前端。您可以从GitHub的链接处,查看到完整的代码。
让我们通过如下示例,来更好地理解Node.js事件循环的调用顺序。
让我们从如下简单的示例开始(如下图所示):
示例1-同步代码
在此,我们有三个功能函数。由于它们是同步的,因此代码会从上至下顺次执行。也就是说,如果三个函数的调用顺序为:first、second、third,它们的代码也会以相同的顺序去执行:first、second、third。
接下来,我们会在第二个示例中引入setTimeout的概念(如下图所示):
示例2-setTimeout
在此,我们先调用first函数,然后在延迟0毫秒后计划调用带有setTimeout的second函数,最后调用third函数。那么,这些函数的执行顺序就变成了:first、third、second。您一定会好奇:为什么second函数会被最后执行呢?
下面让我们来理解两个重要的原则。首先,使用带有延迟值的setTimeout方法,并不意味着应用将在指定毫秒数后,立即执行回调函数。实际上,该值表示的是:执行回调之前,需要经过的最短时间。其次,使用setTimeout来为回调设定的后期执行时间,会在事件循环的每一次迭代期间中始终执行该规则。因此,在事件循环的第一次迭代中,first函数被执行,second函数被“安排”(scheduled),third函数再被执行。然而,在事件循环的第二次迭代期间中,0毫秒的最小延迟已被满足,因此second函数便会在第二次迭代的“计时器”阶段被执行。
然后,我们会在第三个示例中引入setImmediate的概念(如下图所示):
示例3-setImmediate与setTimeout
在该示例中,我们执行first函数,使用setTimeout来为second函数延迟0毫秒,然后使用setImmediate来“安排”third函数。那么,在代码执行的过程中,就会出现一个问题:到底是哪种类型的安排优先?setTimeout还是setImmediate?
鉴于前面已经讨论过setTimeout的工作机制,我们来简单介绍一下setImmediate方法。该方法在事件循环的下一次迭代的“检查”阶段,会去执行其回调函数。因此,如果setImmediate在事件循环的第一次迭代期间被调用,那么它的回调方法会被“安排”上,并在事件循环的第二次迭代期间,执行该回调方法。
正如你在输出中所看到的那样,在我们的示例中,由于被setImmediate安排的回调先于被setTimeout安排的回调执行,因此该示例函数的执行顺序为:first、third、second。
当然,由setImmediate和setTimeout安排的执行到底谁先谁后,实际上取决于被调用方法的上下文。当从Node.js脚本中的主模块,直接调用这两种方法时,其时间取决于进程的性能,因此在每次运行脚本时,回调都可以按照不同的顺序被执行。不过,在I/O周期内调用这些方法时,setImmediate回调总是发生在setTimeout回调之前。在我们上述示例中,由于这些方法是作为响应API端点的某个部分被调用的,因此setImmediate回调会始终在setTimeout回调之前被执行。
为了实现快速的健全性检查,我们使用setImmediate和setTimeout来构建另一个示例(如下图所示)。
示例4-再次使用setImmediate与setTimeout
在此示例中,我们使用setImmediate来安排first函数,接着直接执行second函数,然后使用setTimeout的0毫秒延迟来安排third函数。您恐怕已经猜到了,上述函数的执行顺序为:second、first、third。而在事件循环的第二次迭代中,second函数被setImmediate安排在该I/O周期内被执行,然后third函数在延迟0毫秒时间后也被执行了。
下面,我们将process.nextTick方法引入最后一个示例(如下图所示)。
示例5-process.nextTick
在该示例中,我们使用setImmediate来安排first函数,并使用process.nextTick来安排second函数,再使用带有0毫秒延迟的setTimeout来安排third函数,最后执行fourth函数。那么,在代码运行后,整体的调用顺序为:fourth、second、first、third。
有了前面的基础,我们很容易理解fourth函数为何被首先执行了。毕竟它是被直接调用的,而无需通过任何其他方法来进行安排。process.nextTick方法安排了second函数在第二个被执行,first函数紧接其后。最后被执行的是third函数,其原因在于,在同一个I/O周期内,由setImmediate安排的回调会先于setTimeout安排的回调去执行。
那么,为什么由process.nextTick安排的second函数会先于由setImmediate安排的first函数被执行呢?请不要被这两种方法的名称所误导,并非setImmediate就代表着回调一定会被立即执行,而process.nextTick就一定要等到事件循环的下一轮再执行回调。在此,我们并不展开讨论,如果您有兴趣的话,请参见https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#process-nexttick-vs-setimmediate。您只需注意的是:process.nextTick是在安排的同一阶段中,立即执行回调的;而setImmediate的回调则是在事件循环的下一次迭代、或计时期间中被执行的。
通过上述示例,您应该对Node.js的事件循环,以及诸如setTimeout、setImmediate和process.nextTick等方法有所了解了。当然,您不必深究Node.js的内部结构,以及处理命令的相关操作。我们完全可以将Node.js视为一个黑匣子,轻松地用好Node.js事件循环的各项调用顺序即可。为了进一步了解上面提到的各种示例,您可以通过链接—https://nodejs-event-loop-demo.herokuapp.com/来查看其demo应用,或者通过链接—https://github.com/thawkin3/nodejs-event-loop-demo查看它们在GitHub上的代码。您甚至可以通过参考--https://heroku.com/deploy?template=https://github.com/thawkin3/nodejs-event-loop-demo,将代码部署到Heroku处。
标题名称:如何理解Node.js的事件循环
标题URL:http://www.shufengxianlan.com/qtweb/news2/76252.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联