js的settimeout方法 settimeout函数用法( 四 )


setImmediate和setTimeout上面的这个流程说简单点就是在一个异步流程里,setImmediate会比定时器先执行,我们写点代码来试试:
console.log('outer');setTimeout(() => {setTimeout(() => {console.log('setTimeout');}, 0);setImmediate(() => {console.log('setImmediate');});}, 0);上述代码运行如下:
和我们前面讲的一样,setImmediate先执行了 。我们来理一下这个流程:
外层是一个setTimeout,所以执行他的回调的时候已经在timers阶段了处理里面的setTimeout,因为本次循环的timers正在执行,所以他的回调其实加到了下个timers阶段处理里面的setImmediate,将它的回调加入check阶段的队列外层timers阶段执行完,进入pending callbacks,idle, prepare,poll,这几个队列都是空的,所以继续往下到了check阶段,发现了setImmediate的回调,拿出来执行然后是close callbacks,队列时空的,跳过又是timers阶段,执行我们的console
但是请注意我们上面console.log(‘setTimeout’)和console.log(‘setImmediate’)都包在了一个setTimeout里面,如果直接写在最外层会怎么样呢?代码改写如下:
console.log('outer');setTimeout(() => {console.log('setTimeout');}, 0);setImmediate(() => {console.log('setImmediate');});我们来运行下看看效果:
好像是setTimeout先输出来,我们多运行几次看看:
怎么setImmediate又先出来了,这代码是见鬼了还是啥?这个世界上是没有鬼怪的,所以事情都有原因的,我们顺着之前的Event Loop再来理一下 。在理之前,需要告诉大家一件事情,node.js里面setTimeout(fn, 0)会被强制改为setTimeout(fn, 1),这在官方文档中有说明 。(说到这里顺便提下,HTML 5里面setTimeout最小的时间限制是4ms) 。原理我们都有了,我们来理一下流程:
外层同步代码一次性全部执行完,遇到异步API就塞到对应的阶段遇到setTimeout,虽然设置的是0毫秒触发,但是被node.js强制改为1毫秒,塞入times阶段遇到setImmediate塞入check阶段同步代码执行完毕,进入Event Loop先进入times阶段,检查当前时间过去了1毫秒没有,如果过了1毫秒,满足setTimeout条件,执行回调,如果没过1毫秒,跳过跳过空的阶段,进入check阶段,执行setImmediate回调
通过上述流程的梳理,我们发现关键就在这个1毫秒,如果同步代码执行时间较长,进入Event Loop的时候1毫秒已经过了,setTimeout执行,如果1毫秒还没到,就先执行了setImmediate 。每次我们运行脚本时,机器状态可能不一样,导致运行时有1毫秒的差距,一会儿setTimeout先执行,一会儿setImmediate先执行 。但是这种情况只会发生在还没进入timers阶段的时候 。像我们第一个例子那样,因为已经在timers阶段,所以里面的setTimeout只能等下个循环了,所以setImmediate肯定先执行 。同理的还有其他poll阶段的API也是这样的,比如:
var fs = require('fs')fs.readFile(__filename, () => {setTimeout(() => {console.log('setTimeout');}, 0);setImmediate(() => {console.log('setImmediate');});});这里setTimeout和setImmediate在readFile的回调里面,由于readFile回调是I/O操作,他本身就在poll阶段,所以他里面的定时器只能进入下个timers阶段,但是setImmediate却可以在接下来的check阶段运行,所以setImmediate肯定先运行,他运行完后,去检查timers,才会运行setTimeout 。
类似的,我们再来看一段代码,如果他们两个不是在最外层,而是在setImmediate的回调里面,其实情况跟外层一样,结果也是随缘的,看下面代码:
console.log('outer');setImmediate(() => {setTimeout(() => {console.log('setTimeout');}, 0);setImmediate(() => {console.log('setImmediate');});});原因跟写在最外层差不多,因为setImmediate已经在check阶段了,里面的循环会从timers阶段开始,会先看setTimeout的回调,如果这时候已经过了1毫秒,就执行他,如果没过就执行setImmediate 。


以上关于本文的内容,仅作参考!温馨提示:如遇健康、疾病相关的问题,请您及时就医或请专业人士给予相关指导!

「四川龙网」www.sichuanlong.com小编还为您精选了以下内容,希望对您有所帮助: