node定时器介绍

node定时器简介

node的定时器共有四种,分别是:
setTimeout()
setInterval()
setImmediate()
process.nextTick()
前两个是语言的标准,后两个是node独有的。
node的异步任务可以分为两种,分别是追加在 本轮循环 的异步任务和追加在 次轮循坏 的异步任务。本轮循环一定早于次轮循坏执行。
node规定,process.nextTickPromise的回调函数,追加在本轮循环,即同步任务一旦执行完成就开始执行他们。而setTimeoutsetIntervalsetImmediate的回调函数追加在次轮循坏

process.nextTick & 微任务

process.nextTick是在本轮循环执行的,而且是所有异步任务里面最快执行的,如果希望异步任务尽可能快地执行那就使用它。
根据语言规格,Promise对象的回调函数,会进入异步任务里面的“微任务”(microtask)队列
微任务队列追加在process.nextTick队列的后面,也属于本轮循环。

注:只有前一个队列全部清空以后,才会执行下一个队列,而且process.nextTick的回调函数,执行都会早于Promise的。

事件循环的概念

首先,node只有一个主线程,事件循环是在主线程上完成的。
其次,Node 开始执行脚本时,会先进行事件循环的初始化,但是这时事件循环还没有开始,会先完成同步任务、发出异步请求、规划定时器生效的时间、执行process.nextTick()等等事情,然后才开始事件循环。事件循环会无限次地执行,一轮又一轮。只有异步任务的回调函数队列清空了,才会停止执行。每一轮的事件循环,分成六个阶段,这些阶段会依次执行,如下图。每个阶段都有一个先进先出的回调函数队列。只有一个阶段的回调函数队列清空了,该执行的回调函数都执行了,事件循环才会进入下一个阶段。

事件循环六个阶段

关于 setTimeout 和 setImmediate 执行顺序

一般来说,由于setTimeout在 timers 阶段执行,而setImmediate在 check 阶段执行。所以,setTimeout会早于setImmediate完成。但也可能存在一些特殊情况是的setImmediate早于setTimeout完成,例如

setTimeout(()=> console.log(1));setImmediate(()=> console.log(2));

上面代码应该先输出1,再输出2,但是实际执行的时候,结果却是不确定,有时还会先输出2,再输出1。
这是因为setTimeout的第二个参数默认为0。但是实际上,Node 做不到0毫秒,最少也需要1毫秒,根据官方文档,第二个参数的取值范围在1毫秒到2147483647毫秒之间。也就是说,setTimeout(f, 0)等同于setTimeout(f, 1)。
实际执行的时候,进入事件循环以后,有可能到了1毫秒,也可能还没到1毫秒,取决于系统当时的状况。如果没到1毫秒,那么 timers 阶段就会跳过,进入 check 阶段,先执行setImmediate的回调函数。

但是,下面的代码一定是先输出2,再输出1。

const fs =require('fs');
fs.readFile('test.js',()=>{setTimeout(()=> console.log(1));setImmediate(()=> console.log(2));});

上面代码会先进入I/O callbacks 阶段,然后是check 阶段,最后才是timers阶段。因此,setImmediate才会早于setTimeout执行。

总结

基于此,node定时器的执行顺序大致为:同步任务->process.nextTick->promise->setTimeout->setImmediate

setTimeout(()=> console.log(1));
setImmediate(()=> console.log(2));
process.nextTick(()=> console.log(3));
Promise.resolve().then(()=> console.log(4));
(()=> console.log(5))();
process.nextTick(()=> console.log(6));

因此,上述例子输出为 5,3,6,4,1,2




参考链接:Node定时器详解-阮一峰的网络日志

扫描二维码关注公众号,回复: 3981953 查看本文章

猜你喜欢

转载自blog.csdn.net/qq_32682137/article/details/82629321