EVENT-LOOP
前言(引言):
JavaScript 作为一门单线程语言,却能高效处理高并发任务,其秘密就在于事件循环(Event Loop)机制。你是否曾困惑:
- 为什么
setTimeout(fn, 0)
不立即执行? Promise
和setTimeout
的回调谁先触发?- 事件循环如何像交通指挥员一样调度异步任务
- 宏任务与微任务的优先级博弈(为什么 Promise 总是先于 setTimeout)
- 无论你是想通过面试,还是优化高性能应用,理解事件循环都是进阶 JavaScript 开发的必经之路。现在,让我们从最简单的任务队列说起……
进程线程
1. 进程:
CPU 在运行指令和保存上下文所需要的时间
eg: 手机打开微信,微信系统在执行打开指令,到加载微信的上下文环境(源代码),直到彻底关闭微信之前的这段时间,都是在一个进程中
2. 线程:
是一个进程中的一个更小的单位,指的是执行一段指令所需的时间。
eg: 打开微信聊天界面,就需要一个渲染线程,同时获取到最新的消息,需要一个网络线程
js 是单线程的: 是js在执行一份代码时只会进行单线程
面试题:
浏览器新开一个 tab 页面,就是一个新的进程,这个进程中有很多线程相互配合工作,最后展示页面给到用户。这个过程涉及到的线程至少有:
http 线程
js 引擎线程
渲染线程
异步
- V8 在执行 js 代码时默认只开启一个线程工作(js 是单线程的),所以考虑到执行效率,V8 会先执行同步代码,遇到异步代码时,会将异步代码存放到任务队列,等待 js 引擎空闲时,再从任务队列中取出异步代码的执行。
EVENT-LOOP
微任务
promise.then
process.nextTick(node 里面独有的一个方法,浏览器里面没有)
MutationObserver
宏任务
setTimeout
setInterval
ajax
setImmediate(已被弃用)
I/O (输入/输出)
UI rendering (页面渲染)
执行顺序:
先执行同步代码(属于宏任务),这个过程中遇到异步,就存入任务队列
同步执行完毕后,限制性微任务队列中的代码
微任务全部执行完毕后,有需要的情况下渲染页面
渲染完毕后,执行宏任务队列中的代码(开启了下一个事件循环)
await
function a() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('a');
resolve()
}, 1000)
})
}
function b() {
console.log('b');
}
async function foo() {
await a()
b()
}
foo()
async function foo() { await a() b() } foo() 类似于.then()函数
会将后续的代码挤入微任务队列
浏览器将 await 的执行时间提前了(await 后面的代码要当初同步来看待)
实例训练
console.log(1);
new Promise((resolve) => {
console.log(2);
resolve();
})
.then(() => {
console.log(3);
setTimeout(() => {
console.log(4);
}, 0);
});
setTimeout(() => {
console.log(5);
setTimeout(() => {
console.log(6);
}, 0);
}, 0);
console.log(7);
答案:1 2 7 3 5 4 6
图解:
console.log('script start');
async function async1() {
await async2()
console.log('async1 end');
}
async function async2() {
console.log('async2 end');
}
async1()
setTimeout(() => {
console.log('setTimeout');
}, 0)
new Promise((resolve, reject) => {
console.log('promise');
resolve()
})
.then(() => {
console.log('then1');
})
.then(() => {
console.log('then2');
});
console.log('script end');
答案:script start ---> async2 end ---> promise ---> script end ---> async1 end ---> then1 ---> then2 ---> setTimeout
图解:
看懂图解才是关键