前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解JavaScript的Event-Loop机制

深入理解JavaScript的Event-Loop机制

原创
作者头像
伯爵
修改2019-10-14 17:11:21
6280
修改2019-10-14 17:11:21
举报
文章被收录于专栏:前端小菜鸟

JavaScript 是单线程的,只有JS引擎线程执行事件队列的事件。为了防止代码阻塞,JavaScript使用了异步执行机制。

参与JS代码执行过程的线程有4个:

  1. JS引擎线程:解析和执行JS脚本主线程
  2. 事件触发线程:浏览器内核进程,主要用于控制事件(比如:键盘事件),当监听到事件触发,事件触发线程会将,事件的处理函数push到事件队列,等待主线程执行。
  3. 定时器线程:管理setInterval和setTimeout,当定时器计时完毕,将回调函数push进事件队列等待执行。
  4. HTTP异步请求线程:通过监听XMLHttpRequest连接的readyState状态变更,将该状态的回调函数push到事件队列中,等待执行。
Event-Loop机制

Event-Loop是实现JavaScript异步的一种机制。

JavaScript 事件队列分为两种:宏任务(macro-task)队列和微任务(micro-task)队列。

  • 宏任务(macro-task)是离散的独立任务,比如创建DOM对象,执行全局JavaScript代码,宏任务执行完成之后可以执行其他事件,比如浏览器垃圾回收。

常见的宏任务:I/O,setTimeout,setInterval,执行全局JavaScript代码,UI交互事件,postMessage,setImmediate(Node.js)

  • 微任务(micro-task)是更小的任务,在其他任务执行之前执行,比如Promise执行方法,微任务一般通过异步执行或者需要立即执行的并且不产全新的微任务的事件。微任务是在浏览器UI重新渲染之前执行。

常见的微任务:Promise, MutationObserver,process.nextTick(Node.js)

事件循环通过两个原则处理浏览器事件,一是单线程处理方式,二是事件在执行过程中不会被其他事件中断(除非浏览器自己决定主动终端某个事件,比如浏览器主动关闭一些处理事件时间过长的事件进程,一般很少发生)。

事件循环过程:

  1. 事件循环首先检查宏队列列表,如果队列存在等待宏任务,则执行(2),否则直接执行(3)。
  2. 执行宏任务列表的第1个等待处理事件,执行完成从宏任务队列移除该事件,执行(3)。
  3. 执行微任务列表的第1个处理事件,处理完成从微任务列表移除该事件,如果还有等待的微任务,则重复(3)直到微任务列表为空,否则直接执行(4)。
  4. 检查是否需要更新UI视图,如果是则执行(5),否则返回(1)开始新的循环过程。
  5. UI页面渲染,返回(1)开始新的循环过程。

在事件循环一个完整的迭代过程中,宏任务最多只执行一次,微任务队列则全部被执行,微任务主要目的是为了在下一次UI重绘之前更新程序状态。

微任务优先处理权,微任务队列执行完成之前会阻止浏览器UI渲染。

UI渲染发生在两个宏任务之间,并且UI渲染开始时微任务队列为空。

JavaScript 的事件队列的执行和添加是两个完全独立的过程,确保在事件循环过程中将浏览器监听到的新事件添加到对应事件队列中去,当前执行的事件处理不受影响。

实例分析
代码语言:txt
复制
console.log('js1');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('js2');

我们来分析上面代码执行过程:

  1. 执行 console.log('js1'),js引擎任务该任务同步宏任务 ,直接输出执行结果:js1;
  2. 执行setTimeout(function() {console.log('setTimeout');}, 0),js引擎认为是异步任务,开启定时器线程控制setTimeout,W3C规范规定setTimeout时间间隔最小为4ms,当计时器到4ms时将回调函数push 到事件队列,等待执行。js主线程向下执行;
  3. 执行Promise.resolve().then(function() {console.log('promise1');}).then(function(console.log('promise2');}),js引擎判断Promise为微任务,将该任务push进微笑任务列表,等待宏同步任务执行完毕执行。
  4. 执行console.log('js2'),js引擎判断该任务为宏同步任务,立即执行宏任务,输出:js2
  5. 依次执行微任务列表的所有回调函数,分别输出:promise1,promise2
  6. 微任务为空,执行下一个宏任务,定时器回到,输出:setTimeout
代码语言:txt
复制
// 运行结果
js1
js2
promise1
promise2
setTimeout
参考
  1. JS浏览器事件循环机制
  2. JavaScript忍者秘籍(第二版)
  3. 详解JavaScript中的Event Loop(事件循环)机制
  4. js引擎的执行过程(二)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Event-Loop机制
  • 实例分析
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档