- 定义:同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。
- 任务队列
事件循环是通过任务队列的机制进行协调的。一个事件循环中可以有一个或多个任务队列,一个任务队列便是一系列有序任务的集合,每个任务都有一个任务源,源自同一个任务源的task必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise等API便是任务源,而进入任务队列的是他们指定的具体的执行任务。 - 在事件循环中,每进行一次循环操作称为tick,通过阅读规范可知,每一次 tick 的任务处理模型是比较复杂的,其关键的步骤可以总结如下:
(1).在此次 tick 中选择最先进入队列的任务( oldest task ),如果有则执行(一次)
(2).检查是否存在 Microtasks ,如果存在则不停地执行,直至清空Microtask Queue
(3).更新渲染
(4).主线程重复执行上述步骤 - 宏任务和微任务
异步任务分为两大类, 分别是 Macro Task(宏任务)和 Micro Task(微任务), 并且每个宏任务结束后, 都要清空所有的微任务。
- 宏任务主要包含:script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
- 微任务主要包含:Promise(then回调函数)、MutaionObserver、process.nextTick(Node.js 环境)、async 函数await下面的代码
- 宏任务和微任务之间的关系
在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。注意:微任务的优先级比宏任务的要高。关系图如下:
即总体执行顺序为:浏览器线程先执行同步任务,途中遇到异步任务就将其加入到等待任务队列中去,然后继续向下执行,等同步任务全部执行完毕后,再去等待任务队列中去将所有可执行的微任务逐个执行,执行完后再拿取第一个先到达执行条件的宏任务来执行,执行完后再去等待任务队列中清理执行完所有已到达执行条件的微任务,然后再拿取下一个宏任务来执行,如果宏任务执行产生微任务或者微任务执行产生宏任务就一样加入到等待任务队列中,然后还是按照主线程每次到等待队列中先执行完所有的微任务再逐个执行宏任务的顺序来走 - 实例
例1:
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
//输出顺序:1 2 4 3
例2:
(1)先setTimeout后Promise
console.log('script start');
setTimeout(function() {
console.log('setTimeout'); // 执行回调一个宏任务
}, 0);
Promise.resolve().then(function() {
console.log('promise1'); // 微任务1
}).then(function() {
console.log('promise2'); // 微任务2
});
console.log('script end');
// 结果输出顺序:①script start ②script end ③promise1 ④promise2 ⑤setTimeout
(2)先setTimeout后Promise,Promise中交叉setTimeout
console.log('script start');
setTimeout(function() {
console.log('timeout1'); // 宏任务1
}, 10);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 10); // 宏任务2
}).then(function() {
console.log('then1') //微任务1
})
console.log('script end');
// 结果输出顺序:
script start, promise1, script end, then1, timeout1, timeout2