前端碎碎念 之 nextTick, setTimeout 以及 setImmediate 三者的执行顺序
『前端碎碎念』系列会记录我平时看书或者看文章遇到的问题,一般都是比较基础但是容易遗忘的知识点,你也可能会在面试中碰到。 我会查阅一些资料并可能加上自己的理解,来记录这些问题。更多文章请前往我的个人博客
这个问题是有关执行顺序和Event Loop的。关于Event Loop和任务队列等概念,可以先阅读我引用中的文章,本文主要分析一些存在的疑惑点。
下面这个例子比较典型:
setImmediate(function(){console.log(1);
},0);
setTimeout(function(){console.log(2);
},0);
new Promise(function(resolve){console.log(3);resolve();console.log(4);
}).then(function(){console.log(5);
});
console.log(6);
process.nextTick(function(){console.log(7);
});
console.log(8);//输出结果是3 4 6 8 7 5 2 1
在解释输出结果之前,我们来看几个概念:
macro-task: script (整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering.
micro-task: process.nextTick, Promise(原生),Object.observe,MutationObserver
除了script整体代码,micro-task的任务优先级高于macro-task的任务优先级。
其中,script(整体代码) ,可以理解为待执行的所有代码。
所以执行顺序如下:
第一步. script整体代码被执行,执行过程为
创建setImmediate macro-task
创建setTimeout macro-task
创建micro-task Promise.then 的回调,并执行script console.log(3); resolve(); console.log(4); 此时输出3和4,虽然resolve调用了,执行了但是整体代码还没执行完,无法进入Promise.then 流程。
console.log(6)输出6
process.nextTick 创建micro-task
console.log(8) 输出8
第一个过程过后,已经输出了3 4 6 8
第二步. 由于其他micro-task 的 优先级高于macro-task。
此时micro-task 中有两个任务按照优先级process.nextTick 高于 Promise。
所以先输出7,再输出5
第三步,micro-task 任务列表已经执行完毕,家下来执行macro-task. 由于setTimeout的优先级高于setIImmediate,所以先输出2,再输出1。
整个过程描述起来像是同步操作,实际上是基于Event Loop的事件循环。
关于micro-task和macro-task的执行顺序,可看下面这个例子(来自《深入浅出Node.js》):
//加入两个nextTick的回调函数
process.nextTick(function () {console.log('nextTick延迟执行1');
});
process.nextTick(function () { console.log('nextTick延迟执行2');
});
// 加入两个setImmediate()的回调函数
setImmediate(function () {console.log('setImmediate延迟执行1'); // 进入下次循环 process.nextTick(function () {console.log('强势插入');});
});
setImmediate(function () {console.log('setImmediate延迟执行2');
});console.log('正常执行');
书中给出的执行结果是:
正常执行
nextTick延迟执行1
nextTick延迟执行2
setImmediate延迟执行1
强势插入
setImmediate延迟执行2
process.nextTick在两个setImmediate之间强行插入了。
但运行这段代码发现结果却是这样:
正常执行
nextTick延迟执行1
nextTick延迟执行2
setImmediate延迟执行1
setImmediate延迟执行2
强势插入
朴老师写那本书的时候,node最新版本为0.10.13,而我的版本是6.x
老版本的Node会优先执行process.nextTick。
当process.nextTick队列执行完后再执行一个setImmediate任务。然后再次回到新的事件循环。所以执行完第一个setImmediate后,队列里只剩下第一个setImmediate里的process.nextTick和第二个setImmediate。所以process.nextTick会先执行。
而在新版的Node中,process.nextTick执行完后,会循环遍历setImmediate,将setImmediate都执行完毕后再跳出循环。所以两个setImmediate执行完后队列里只剩下第一个setImmediate里的process.nextTick。最后输出"强势插入"。
具体实现可参考Node.js源码。
关于优先级的另一个比较清晰的版本:
观察者优先级
在每次轮训检查中,各观察者的优先级分别是:
idle观察者 > I/O观察者 > check观察者。
idle观察者:process.nextTick
I/O观察者:一般性的I/O回调,如网络,文件,数据库I/O等
check观察者:setImmediate,setTimeout
setImmediate 和 setTimeout 的优先级
看下面这个例子:
setImmediate(function () {console.log('1');
});
setTimeout(function () {console.log('2');
}, 0);console.log('3');//输出结果是3 2 1
我们知道现在HTML5规定setTimeout的最小间隔时间是4ms,也就是说0实际上也会别默认设置为最小值4ms。我们把这个延迟加大
上面说到setTimeout 的优先级比 setImmediate的高,其实这种说法是有条件的。
再看下面这个例子,为setTimeout增加了一个延迟20ms的时间:
setImmediate(function () {console.log('1');
});
setTimeout(function () {console.log('2');
}, 20);console.log('3');//输出结果是3 2 1
setTimeout延迟20ms再执行,而setImmediate是立即执行,竟然2比1还先输出??
试试打印出这个程序的执行时间:
var t1 = +new Date();
setImmediate(function () {console.log('1');
});
setTimeout(function () {console.log('2');
},20);console.log('3');
var t2 = +new Date();
console.log('time: ' + (t2 - t1));
//输出
3
time: 23
2
1
程序执行用了23ms, 也就是说,在script(整体代码)执行完之前,setTimeout已经过时了,所以当进入macro-task的时候setTimeout依然优先于setImmediate执行。如果我们把这个值调大一点呢?
var t1 = +new Date();
setImmediate(function () {console.log('1');
});
setTimeout(function () {console.log('2');
},30);console.log('3');
var t2 = +new Date();
console.log('time: ' + (t2 - t1));
//输出
3
time: 23
1
2
setImmediate早于setTimeout执行了,因为进入macro-task 循环的时候,setTimeout的定时器还没到。
以上实验是基于6.6.0版本Node.js测试,实际上在碰到类似这种问题的时候,最好的办法是参考标准,并查阅源码,不能死记概念和顺序,因为标准也是会变的。包括此文也是自学总结,经供参考。
参考:
https://www.zhihu.com/questio...
https://segmentfault.com/a/11...
http://www.jianshu.com/p/837b...
前端碎碎念 之 nextTick, setTimeout 以及 setImmediate 三者的执行顺序相关推荐
- 参加海峡两岸城市地理信息系统论坛2010 年会(一张图、规划信息化和空间句法的碎碎念)...
上周末去清华建筑学院开了个会,叫做海峡两岸城市地理信息系统论坛2010 年会,主题很大,但是内容比较集中一些,就是围绕着GIS与城市规划.一天下来听了20个报告,挺佩服主办方的时间控制,这么密集的报告 ...
- Jerry的碎碎念:SAPUI5, Angular, React和Vue
2019独角兽企业重金招聘Python工程师标准>>> 去年我去一个国内客户现场时,曾经和他们IT部门的一位架构师聊到关于在SAP平台上进行UI应用的二次开发时,UI框架是选用UI5 ...
- 每月碎碎念 | 2019.08
Hi,这里是新开辟的"碎碎念"的世界. 这个区域作为记录心情的地方,把每日的所思所想所感所悟记录在这里,当做一个写日记的地方,每个月底汇总成一篇文章,只给我的朋友看. 不知不觉一个 ...
- 菜鸟级测试开发者日记1 2020总结碎碎念看破红尘般的展望
菜鸟级测试开发者日记1 2020总结碎碎念&看破红尘般的展望 我叕回来了. 2020年对全世界来说都是很不友好的一年.疫情搞的裁员的裁员,年终泡汤的泡汤.很多人则是失业+年终泡汤乃至欠薪.相比 ...
- 每月碎碎念 | 2019.7
Hi,这里是新开辟的"碎碎念"的世界. 这个区域作为记录心情的地方,把每日的所思所想所感所悟记录在这里,当做一个写日记的地方,每个月底汇总成一篇文章,只给我的朋友看. 2019-7 ...
- PMcaff写给大家的年终碎碎念 PMcaff | 记录
今天是大年三十,2014马上就要结束了,送上新春祝福之前,碎碎念的小希有话想跟大家说. 瞧这一年 小米在硬件行业继续如鱼得水,科幻片里的智能家居生慢慢变成生活. 阿里巴巴在纳克达斯扬眉吐气了一把,一夜 ...
- 数据结构碎碎念(一)
碎碎念 在大一学习C语言的时候,举过一个用栈实现的括号匹配算法,当时觉得很难,不过现在回顾起来,这个算法也算是比较简单的一个关于栈的应用了.而现在所常见的算法问题也都是什么中缀表达式转后缀表达式,双栈 ...
- 机器学习碎碎念:霍夫丁不等式
点击上方"AI有道",选择"设为星标" 关键时刻,第一时间送达! 红色石头每天碎碎念一些机器学习知识和概念,大家一起学习,每天进步一点点!喜欢的话别忘了文末点赞 ...
- 花 1 个月收入购买一份保险之后,我的一点碎碎念!
大家好,我是小詹,一个长得有些清秀的美男子.今天想跟大家聊一聊保险这件事.(纯属碎碎念,不要担心是广告或者推销哈哈哈) 去年年底开始有给自己购买一份保险的想法,最近经过几番了解对比,终于狠心花了一个月 ...
最新文章
- Bmu计算机,高性能定点DSP位处理单元BMU的 - 处理器/DSP - 电子发烧友网
- Cocos2d-x 生成真正的随机数
- vim 用次数做简单的算术运算(笔记)
- 中国大学生源质量排行榜150强
- 关于Static控件背景透明时文本覆盖重影的问题
- 风吹来_梅花香自苦寒来!一组赏心悦目的梅花图……
- java解压文件、复制文件、删除文件代码示例
- 如何以sandbox模式测试开发好的Fiori Launchpad插件
- .Net Core实现的文档数据库RavenDb
- /dev/socket/vold exploit 本地提权漏洞
- latex数学公式符号 + markdown操作(图片的缩放、居中等)
- python基本操作(四)
- 水土不服的SNS,落地生根的网游
- 液晶电视的驱动板与逻辑板维修
- iOS创建自定义相册
- 网络负能量为何发展如此迅速?
- HTML5中引入字体样式的常用方法-Iconfont(阿里巴巴矢量图库)和IcoMoon-APP
- 定积分求解方法——换元积分法
- 【源码篇】源码阅读集合
- Web开发技术的演变
热门文章
- WinRM设置信任主机
- 剖析PHP中的输出缓冲
- C语言100个经典的算法
- 使用 HttpResponse.Write 方法进行字符串串联
- Inplayable技术分享
- Brute Force算法介绍及C++实现
- 【Qt】Qt动态库和静态库的创建和使用
- 【Qt】Qt程序编译成功,执行时报错:程序异常结束,crashed
- weblogic连接oracle配置文件,配置weblogic连接oracle的数据源
- boost log 能不能循环覆盖_前端基础进阶(十四):深入核心,详解事件循环机制...