https://zhuanlan.zhihu.com/p/41543963
单线程
js是单线程语言,只能同时做一件事
js引擎和Dom操作共用同一个线程
- js为什么是单线程
防止多个线程对于Dom节点造成不可控的修改,降低复杂度,
JavaScript选择只用一个主线程来执行代码,以保证代码执行的一致性;
执行栈
js在解析同步任务的时候,会将这些任务按照执行顺序排列到一个地方,这个地方就叫做执行栈
事件队列
js会将异步任务按照执行顺序,加入到与执行栈不同的另一个队列,也就是事件队列。
浏览器的事件循环
执行步骤:
1、js从上到下解析方法,主线程将同步任务添加到执行栈;
2、当碰到ajax、setTimeout等异步任务时,会暂时挂起,继续执行执行栈中的任务,
等异步任务返回结果,再按照执行顺序排列到事件队列中;
3、主线程先将执行栈中同步任务清空,检查事件队列是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,继续排到事件队列;
4、主线程每次清空执行栈,就去检查事件队列是否有任务,如果有就取出一个推到执行栈,这个过程是循环往复的…这个过程就叫做EventLoop
事件循环。
宏任务和微任务
实际上异步任务之间也不相同,执行优先级也有区别。不同的异步任务被分为两类:
宏任务(macro task)和微任务(micro task)。我们将经常遇到的异步任务进行分类如下:
宏任务:setTimeout,setInterval,setImmediate,I/O(磁盘读写或网络通信),UI交互事件
微任务:process.nextTick,Promise.then
当执行栈中的任务清空,主线程会先检查微任务队列中是否有任务,如果有,就将微任务队列中的任务依次执行,直到微任务队列为空,之后再检查宏任务队列中是否有任务,如果有,则每次取出第一个宏任务加入到执行栈中,之后再清空执行栈,检查微任务,以此循环… …
同一次事件循环中,微任务永远在宏任务之前执行。
一次事件循环只执行处于 Macrotask 队首的任务,执行完成后,立即执行 Microtask 队列中的所有任务。
await后的内容为宏任务,await下的内容为异步任务
async function async1() {
await async2() // 宏任务1 async2() **await后的内容为宏任务,await下的内容为异步任务**
console.log('async1 end') // 微任务1 可以理解为.then里面的内容
}
async function async2() {
console.log('async2 end') // 宏任务1
}
async1() // 宏任务1
// async2 end async1 end
总结
- js解析方法时,将同步任务排队到执行栈中,异步任务排队到事件队列中。
- 事件队列分为:
- 宏任务:setTimeout,setInterval,setImmediate,I/O,UI交互事件
- 微任务:process.nextTick,Promise.then
- 浏览器环境中执行方法时,先将执行栈中的任务清空,再将微任务推到执行栈中并清空,之后检查是否存在宏任务,若存在则取出一个宏任务,执行完成检查是否有微任务,以此循环…