前端js中的事件循环eventloop机制详解


Posted in Javascript onMay 15, 2019

前言

我们知道 js 是单线程执行的,那么异步的代码 js 是怎么处理的呢?例如下面的代码是如何进行输出的:

console.log(1);
setTimeout(function() {
 console.log(2);
}, 0);
new Promise(function(resolve) {
 console.log(3);
 resolve(Date.now());
}).then(function() {
 console.log(4);
});
console.log(5);
setTimeout(function() {
 new Promise(function(resolve) {
  console.log(6);
  resolve(Date.now());
 }).then(function() {
  console.log(7);
 });
}, 0);

在不运行的情况可以先猜测下最终的输出,然后展开我们要说的内容。

1. 宏任务与微任务

依据我们多年编写 ajax 的经验:js 应该是按照语句先后顺序执行,在出现异步时,则发起异步请求后,接着往下执行,待异步结果返回后再接着执行。但他内部是怎样管理这些执行任务的呢?

在 js 中,任务分为宏任务(macrotask)和微任务(microtask),这两个任务分别维护一个队列,均采用先进先出的策略进行执行!同步执行的任务都在宏任务上执行。

宏任务主要有:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。

微任务主要有:Promise.then、 MutationObserver、 process.nextTick(Node.js 环境)。

具体的操作步骤如下:

  1. 从宏任务的头部取出一个任务执行;
  2. 执行过程中若遇到微任务则将其添加到微任务的队列中;
  3. 宏任务执行完毕后,微任务的队列中是否存在任务,若存在,则挨个儿出去执行,直到执行完毕;
  4. GUI 渲染;
  5. 回到步骤 1,直到宏任务执行完毕;

这 4 步构成了一个事件的循环检测机制,即我们所称的eventloop。

回到我们上面说的代码:

console.log(1);
setTimeout(function() {
 console.log(2);
}, 0);
new Promise(function(resolve) {
 console.log(3);
 resolve(Date.now());
}).then(function() {
 console.log(4);
});
console.log(5);
setTimeout(function() {
 new Promise(function(resolve) {
  console.log(6);
  resolve(Date.now());
 }).then(function() {
  console.log(7);
 });
}, 0);

执行步骤如下:

  1. 执行 log(1),输出 1;
  2. 遇到 setTimeout,将回调的代码 log(2)添加到宏任务中等待执行;
  3. 执行 console.log(3),将 then 中的 log(4)添加到微任务中;
  4. 执行 log(5),输出 5;
  5. 遇到 setTimeout,将回调的代码 log(6, 7)添加到宏任务中;
  6. 宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,存在一个微任务 log(4)(在步骤 3 中添加的),执行输出 4;
  7. 取出下一个宏任务 log(2)执行,输出 2;
  8. 宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,不存在;
  9. 取出下一个宏任务执行,执行 log(6),将 then 中的 log(7)添加到微任务中;
  10. 宏任务执行完毕,存在一个微任务 log(7)(在步骤 9 中添加的),执行输出 7;

因此,最终的输出顺序为:1, 3, 5, 4, 2, 6, 7;

我们在Promise.then实现一个稍微耗时的操作,这个步骤看起来会更加地明显:

console.log(1);
var start = Date.now();
setTimeout(function() {
 console.log(2);
}, 0);
setTimeout(function() {
 console.log(4, Date.now() - start);
}, 400);
Promise.resolve().then(function() {
 var sum = function(a, b) {
  return Number(a) + Number(b);
 }
 var res = [];
 for(var i=0; i<5000000; i++) {
  var a = Math.floor(Math.random()*100);
  var b = Math.floor(Math.random()*200);
  res.push(sum(a, b));
 }
 res = res.sort();
 console.log(3);
})

Promise.then中,先生成一个500万随机数的数组,然后对这个数组进行排序。运行这段代码可以发现:马上会输出1,稍等一会儿才会输出3,然后再输出2。不论等待多长时间输出3,2一定会在3的后面输出。这也就印证了eventloop中的第3步操作,必须等所有的微任务执行完毕后,才开始下一个宏任务。

同时,这段代码的输出很有意思:

setTimeout(function() {
 console.log(4, Date.now() - start); // 4, 1380 电脑状态的不同,输出的时间差也不一样
}, 400);

本来要设定的是400ms后输出,但因为之前的任务耗时严重,导致之后的任务只能延迟往后排。也能说明,setTimeout和setInterval这种操作的延时是不准确的,这两个方法只能大概将任务400ms之后的宏任务中,但具体的执行时间,还是要看线程是否空闲。若前一个任务中有耗时的操作,或者有无限的微任务加入进来时,则会阻塞下一个任务的执行。

2. async-await

从上面的代码中也能看到 Promise.then 中的代码是属于微服务,那么 async-await 的代码怎么执行呢?比如下面的代码:

function A() {
  return Promise.resolve(Date.now());
}
async function B() {
  console.log(Math.random());
  let now = await A();
  console.log(now);
}
console.log(1);
B();
console.log(2);

其实,async-await 只是 Promise+generator 的一种语法糖而已。上面的代码我们改写为这样,可以更加清晰一点:

function B() {
  console.log(Math.random());
  A().then(function(now) {
    console.log(now);
  })
}
console.log(1);
B();
console.log(2);

这样我们就能明白输出的先后顺序了: 1, 0.4793526730678652(随机数), 2, 1557830834679(时间戳);

3. requestAnimationFrame

requestAnimationFrame也属于执行是异步执行的方法,但我任务该方法既不属于宏任务,也不属于微任务。按照MDN中的定义:

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

requestAnimationFrame是GUI渲染之前执行,但在微服务之后,不过requestAnimationFrame不一定会在当前帧必须执行,由浏览器根据当前的策略自行决定在哪一帧执行。

4. 总结

我们要记住最重要的两点:js是单线程和eventloop的循环机制。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript cookies操作集合
Apr 12 Javascript
Javascript 网页黑白效果实现代码(兼容IE/FF等)
Apr 23 Javascript
js实现二代身份证号码验证详解
Nov 20 Javascript
JavaScript 面向对象与原型
Apr 10 Javascript
RequireJS多页面应用实例分析
Jun 29 Javascript
JS表单数据验证的正则表达式(常用)
Feb 18 Javascript
Node.JS使用Sequelize操作MySQL的示例代码
Oct 09 Javascript
vue2.0 和 animate.css的结合使用
Dec 12 Javascript
jQuery+Datatables实现表格批量删除功能【推荐】
Oct 24 jQuery
每周一练 之 数据结构与算法(Stack)
Apr 16 Javascript
js表达式与运算符简单操作示例
Feb 15 Javascript
微信小程序完美解决scroll-view高度自适应问题的方法
Aug 08 Javascript
mpvue小程序循环动画开启暂停的实现方法
May 15 #Javascript
微信小程序的tab选项卡的实现效果
May 15 #Javascript
少女风vue组件库的制作全过程
May 15 #Javascript
vue两组件间值传递 $router.push实现方法
May 15 #Javascript
JavaScript+HTML5 canvas实现放大镜效果完整示例
May 15 #Javascript
详解微信UnionID作用
May 15 #Javascript
小程序:授权、登录、session_key、unionId的详解
May 15 #Javascript
You might like
php站内搜索并高亮显示关键字的实现代码
2011/12/29 PHP
探讨:如何通过stats命令分析Memcached的内部状态
2013/06/14 PHP
jQuery+JSON+jPlayer实现QQ空间音乐查询功能示例
2013/06/17 Javascript
jquery ajax 调用失败的原因示例介绍
2013/09/27 Javascript
浅谈javascript六种数据类型以及特殊注意点
2013/12/20 Javascript
Jquery利用mouseenter和mouseleave实现鼠标经过弹出层且可以点击
2014/02/12 Javascript
Ext修改GridPanel数据和字体颜色、css属性等
2014/06/13 Javascript
js变形金刚文字特效代码分享
2015/08/20 Javascript
javascript闭包(Closure)用法实例简析
2015/11/30 Javascript
jQuery遮罩层效果实例分析
2016/01/14 Javascript
浅谈Nodejs中的作用域问题
2016/12/26 NodeJs
获取url中用&amp;隔开的参数实例(分享)
2017/05/28 Javascript
jQuery动态添加.active 实现导航效果代码思路详解
2017/08/29 jQuery
vue-cli项目中怎么使用mock数据
2017/09/27 Javascript
webpack 打包压缩js和css的方法示例
2018/03/20 Javascript
JavaScript设计模式之缓存代理模式原理与简单用法示例
2018/08/07 Javascript
快速解决vue在ios端下点击响应延时的问题
2018/08/27 Javascript
解决vue 引入子组件报错的问题
2018/09/06 Javascript
Vue press 支持图片放大功能的实例代码
2018/11/09 Javascript
JS实现商城秒杀倒计时功能(动态设置秒杀时间)
2019/12/12 Javascript
[01:08:33]OG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
合并Excel工作薄中成绩表的VBA代码,非常适合教育一线的朋友
2009/04/09 Python
python timestamp和datetime之间转换详解
2017/12/11 Python
使用Python来开发微信功能
2018/06/13 Python
python 数字类型和字符串类型的相互转换实例
2018/07/17 Python
Python OpenCV对本地视频文件进行分帧保存的实例
2019/01/08 Python
PyQt5 QListWidget选择多项并返回的实例
2019/06/17 Python
如何用Python做一个微信机器人自动拉群
2019/07/03 Python
Python实现的统计文章单词次数功能示例
2019/07/08 Python
查看端口并杀进程python脚本代码
2019/12/17 Python
Python连接mysql方法及常用参数
2020/09/01 Python
GUESS德国官网:美国牛仔服装品牌
2017/02/14 全球购物
德国EGOIST网店:销售畅销的设计师品牌
2017/04/18 全球购物
求职信的要素有哪些呢
2013/12/26 职场文书
班组长竞聘书
2014/03/31 职场文书
民警个人对照检查剖析材料
2014/09/17 职场文书