开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情
什么是Event Loop
Event Loop 即事件循环(事件轮询),是指浏览器或者Node 的一种解决JavaScript 单线程运行时不阻塞的一种机制,也就是经常使用的异步。
EventLoop 事件循环
掌握了事件循环有助于我们去理解 js 带的运行机制。
事件分类
- 同步任务
- 异步任务
- 宏任务
- 微任务
同步
代码按照上下文执行顺序一次执行。
// 同步任务
console.log(1);
console.log(2);
console.log(3);
异步
- 宏任务
- 微任务
- 同步任务1
- 异步任务
- 宏任务3
- setTimeout
- setInterval
- setImmediate (Node独有)
- requestAnimationFrame (浏览器独有)
- I/O
- UI rendering (浏览器独有)
- 微任务2
- process.nextTick (Node独有)
- Promise.then
- Object.observe
- MutationObserver
- 宏任务3
浏览器对JavaScript代码的执行流程如下
- 执行代码的同步部分
- 把微任务队列里的任务全部执行
- 执行宏任务的第一个任务(执行完有可能产生微任务),如果产生了微任务,将微任务推进微任务队列
- 全局Script代码执行完毕后,调用栈Stack会清空;
- 从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;
- 继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行;
总结:
- 微任务队列优先于宏任务队列执行,(宏任务优先级最低)
- 微任务队列上创建的宏任务会被后添加到当前宏任务队列的尾端,微任务队列中创建的微任务会被添加到微任务队列的尾端。
- 只要微任务队列中还有任务,宏任务队列就只会等待微任务队列执行完毕后再执行
实操一下
const p1 = new Promise((res, rej) => {
console.log("Promise");
res(1);
});
// 第一个宏任务
setTimeout(function () {
console.log(1);
setTimeout(function () {
console.log(2);
p1.then(res => {
console.log(3);
});
}, 0);
console.log(4);
}, 0);
// 第二个微任务
setTimeout(() => {
process.nextTick(() => {
p1.then(res => {
console.log(5);
setTimeout(function () {
console.log(6);
p1.then(res => {
console.log(7);
console.log("2:" + res);
});
console.log(8);
}, 0);
console.log(9);
});
});
});
观察下面代码的执行顺序:
console.log(1);
console.log(3);
Promise.resolve(4).then((res) => {
console.log(res);
});
setTimeout(() => {
console.log(2);
}, 0);
我们可以得知,先执行同步任务,在执行异步任务。
异步任务当中有宏任务、微任务,先执行微任务
宏任务和微任务的优先级
console.log(1);
console.log(3);
Promise.resolve(4).then((res) => {
console.log(res);
});
setTimeout(() => {
console.log(2);
}, 0);
我们可以得知,先执行同步任务,在执行异步任务。
异步任务当中有宏任务、微任务,先执行微任务
宏任务嵌套
console.log(1);
setTimeout(() => {
console.log(7);
setTimeout(() => {
console.log(3);
});
console.log(8);
Promise.resolve(4).then((res) => {
console.log(res);
});
console.log(9);
}, 0);
setTimeout(() => {
console.log(10);
Promise.resolve(5).then((res) => {
console.log(res);
});
console.log(11);
setTimeout(() => {
console.log(6);
});
console.log(12);
});
console.log(2);
// 1 2 789 4 101112 5 3 6