『前端碎碎念』系列会记录我平时看书或者看文章遇到的问题,一般都是比较基础但是容易遗忘的知识点,你也可能会在面试中碰到。 我会查阅一些资料并可能加上自己的理解,来记录这些问题。更多文章请前往我的个人博客

这个问题是有关执行顺序和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 三者的执行顺序相关推荐

  1. 参加海峡两岸城市地理信息系统论坛2010 年会(一张图、规划信息化和空间句法的碎碎念)...

    上周末去清华建筑学院开了个会,叫做海峡两岸城市地理信息系统论坛2010 年会,主题很大,但是内容比较集中一些,就是围绕着GIS与城市规划.一天下来听了20个报告,挺佩服主办方的时间控制,这么密集的报告 ...

  2. Jerry的碎碎念:SAPUI5, Angular, React和Vue

    2019独角兽企业重金招聘Python工程师标准>>> 去年我去一个国内客户现场时,曾经和他们IT部门的一位架构师聊到关于在SAP平台上进行UI应用的二次开发时,UI框架是选用UI5 ...

  3. 每月碎碎念 | 2019.08

    Hi,这里是新开辟的"碎碎念"的世界. 这个区域作为记录心情的地方,把每日的所思所想所感所悟记录在这里,当做一个写日记的地方,每个月底汇总成一篇文章,只给我的朋友看. 不知不觉一个 ...

  4. 菜鸟级测试开发者日记1 2020总结碎碎念看破红尘般的展望

    菜鸟级测试开发者日记1 2020总结碎碎念&看破红尘般的展望 我叕回来了. 2020年对全世界来说都是很不友好的一年.疫情搞的裁员的裁员,年终泡汤的泡汤.很多人则是失业+年终泡汤乃至欠薪.相比 ...

  5. 每月碎碎念 | 2019.7

    Hi,这里是新开辟的"碎碎念"的世界. 这个区域作为记录心情的地方,把每日的所思所想所感所悟记录在这里,当做一个写日记的地方,每个月底汇总成一篇文章,只给我的朋友看. 2019-7 ...

  6. PMcaff写给大家的年终碎碎念 PMcaff | 记录

    今天是大年三十,2014马上就要结束了,送上新春祝福之前,碎碎念的小希有话想跟大家说. 瞧这一年 小米在硬件行业继续如鱼得水,科幻片里的智能家居生慢慢变成生活. 阿里巴巴在纳克达斯扬眉吐气了一把,一夜 ...

  7. 数据结构碎碎念(一)

    碎碎念 在大一学习C语言的时候,举过一个用栈实现的括号匹配算法,当时觉得很难,不过现在回顾起来,这个算法也算是比较简单的一个关于栈的应用了.而现在所常见的算法问题也都是什么中缀表达式转后缀表达式,双栈 ...

  8. 机器学习碎碎念:霍夫丁不等式

    点击上方"AI有道",选择"设为星标" 关键时刻,第一时间送达! 红色石头每天碎碎念一些机器学习知识和概念,大家一起学习,每天进步一点点!喜欢的话别忘了文末点赞 ...

  9. 花 1 个月收入购买一份保险之后,我的一点碎碎念!

    大家好,我是小詹,一个长得有些清秀的美男子.今天想跟大家聊一聊保险这件事.(纯属碎碎念,不要担心是广告或者推销哈哈哈) 去年年底开始有给自己购买一份保险的想法,最近经过几番了解对比,终于狠心花了一个月 ...

最新文章

  1. Bmu计算机,高性能定点DSP位处理单元BMU的 - 处理器/DSP - 电子发烧友网
  2. Cocos2d-x 生成真正的随机数
  3. vim 用次数做简单的算术运算(笔记)
  4. 中国大学生源质量排行榜150强
  5. 关于Static控件背景透明时文本覆盖重影的问题
  6. 风吹来_梅花香自苦寒来!一组赏心悦目的梅花图……
  7. java解压文件、复制文件、删除文件代码示例
  8. 如何以sandbox模式测试开发好的Fiori Launchpad插件
  9. .Net Core实现的文档数据库RavenDb
  10. /dev/socket/vold exploit 本地提权漏洞
  11. latex数学公式符号 + markdown操作(图片的缩放、居中等)
  12. python基本操作(四)
  13. 水土不服的SNS,落地生根的网游
  14. 液晶电视的驱动板与逻辑板维修
  15. iOS创建自定义相册
  16. 网络负能量为何发展如此迅速?
  17. HTML5中引入字体样式的常用方法-Iconfont(阿里巴巴矢量图库)和IcoMoon-APP
  18. 定积分求解方法——换元积分法
  19. 【源码篇】源码阅读集合
  20. Web开发技术的演变

热门文章

  1. WinRM设置信任主机
  2. 剖析PHP中的输出缓冲
  3. C语言100个经典的算法
  4. 使用 HttpResponse.Write 方法进行字符串串联
  5. Inplayable技术分享
  6. Brute Force算法介绍及C++实现
  7. 【Qt】Qt动态库和静态库的创建和使用
  8. 【Qt】Qt程序编译成功,执行时报错:程序异常结束,crashed
  9. weblogic连接oracle配置文件,配置weblogic连接oracle的数据源
  10. boost log 能不能循环覆盖_前端基础进阶(十四):深入核心,详解事件循环机制...