Appearance
事件循环
总结:事件循环是 JS 的运行机制,JS 是单线程的,意味着所有任务都需要排队依次执行,前一个任务未完成时后续任务不能被执行。为了解决任务阻塞后续任务执行就有了同步和异步任务,异步任务交给其他线程中处理,当异步任务需要执行将其放到主线程中执行。事件循环具体流程....
了解事件循环需要先知道 JS 为什么拥有事件循环机制,看看 JS 拥有事件循环的历史原因。
1.JS 是单线程的
单线程意味着 Web 应用的所有代码都是在一个线程中运行的,同一时间只能做一件事情。为什么不能是多线程的?因为 JS 是需要操作 DOM 的,若某个线程通过 DOM 删除了一个元素而另一个线程有在这个元素中添加了子元素,那么浏览器以哪个线程的操作为准?为了降低复杂性,所以 JS 只能是单线程的。
2.任务队列
单线程就意味着所有任务都需要排队执行,前一个任务结束,后面的任务才能执行。若前一个任务耗费事件长,后续任务也需要等待前一个任务完成才能被执行。例如网络请求,就需要等待响应成功才能执行后续任务,会导致后续任务的阻塞。
JavaScript 语言的设计者意识到,这时主线程完全可以不管 IO 设备,挂起处于等待中的任务,先运行排在后面的任务。等到 IO 设备返回了结果,再回过头,把挂起的任务继续执行下去。
后来,JS 有了同步任务、异步任务的把原先的任务区分开来:
同步任务:主线程中排队执行的任务,只有前一个同步任务完成,才能执行后续同步任务。
异步任务:异步任务不会阻塞同步任务的执行,会将异步任务放到浏览器其他线程中处理,当异步任务有了结果后,会将其放到任务队列里等待主线程执行。
任务队列:是用来存放异步任务的。
3.事件循环
整个 JS 应用的执行流程如下:
- 主线程执行同步任务
- 遇到异步操作时将异步任务注册给
event table
,待合适时会入队到任务队列event queue
中 - 主线程同步任务执行完成,查看任务队列中是否有需要执行的任务
- 若有,出队然后执行一个任务;若无,执行完毕。
- 主线程不断重复步骤 3、4
整个执行流程就是指事件循环,事件循环就是 JS 的运行机制。
4.任务优先级
在 node 环境下,任务是有优先级的:
js
process.nextTick()>Promise.then()>setTimeout>setImmediate。
process.nextTick()>Promise.then()>setTimeout>setImmediate。
只需要知道nextTick
比then
注册的任务有更高优先级。
5.微任务须知
WARNING
若在本次循环的某个微任务执行时,又创建了微任务,则这个新的微任务会添加到本次循环的微任务队尾中,等待执行。而不会将该微任务添加到下次循环的微任务队列中。
js
console.log("script start");
Promise.resolve().then(() => {
console.log("then01");
Promise.resolve().then(() => {
console.log("then02");
});
});
Promise.resolve().then(() => {
console.log("then03");
});
setTimeout(() => {
console.log("setTimeout");
Promise.resolve().then(() => {
console.log("then");
});
});
console.log("script end");
console.log("script start");
Promise.resolve().then(() => {
console.log("then01");
Promise.resolve().then(() => {
console.log("then02");
});
});
Promise.resolve().then(() => {
console.log("then03");
});
setTimeout(() => {
console.log("setTimeout");
Promise.resolve().then(() => {
console.log("then");
});
});
console.log("script end");
6.宏微任务相关 API
宏
setTimeout、setInterval、requestAnimationFrame、IO 操作、UI 渲染
微
then、queueMicrotask、Observer、nextTick