https://zhuanlan.zhihu.com/p/41543963

单线程

js是单线程语言,只能同时做一件事
js引擎和Dom操作共用同一个线程

  • js为什么是单线程

防止多个线程对于Dom节点造成不可控的修改,降低复杂度,
JavaScript选择只用一个主线程来执行代码,以保证代码执行的一致性;

执行栈

js在解析同步任务的时候,会将这些任务按照执行顺序排列到一个地方,这个地方就叫做执行栈

事件队列

js会将异步任务按照执行顺序,加入到与执行栈不同的另一个队列,也就是事件队列。

浏览器的事件循环

image.png
执行步骤:
1、js从上到下解析方法,主线程将同步任务添加到执行栈;
2、当碰到ajax、setTimeout等异步任务时,会暂时挂起,继续执行执行栈中的任务,
等异步任务返回结果,再按照执行顺序排列到事件队列中;
3、主线程先将执行栈中同步任务清空,检查事件队列是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,继续排到事件队列;
4、主线程每次清空执行栈,就去检查事件队列是否有任务,如果有就取出一个推到执行栈,这个过程是循环往复的…这个过程就叫做EventLoop事件循环。

宏任务和微任务

实际上异步任务之间也不相同,执行优先级也有区别。不同的异步任务被分为两类:
宏任务(macro task)和微任务(micro task)。我们将经常遇到的异步任务进行分类如下:
宏任务:setTimeout,setInterval,setImmediate,I/O(磁盘读写或网络通信),UI交互事件
微任务:process.nextTick,Promise.then

image.png

当执行栈中的任务清空,主线程会先检查微任务队列中是否有任务,如果有,就将微任务队列中的任务依次执行,直到微任务队列为空,之后再检查宏任务队列中是否有任务,如果有,则每次取出第一个宏任务加入到执行栈中,之后再清空执行栈,检查微任务,以此循环… …

同一次事件循环中,微任务永远在宏任务之前执行。

一次事件循环只执行处于 Macrotask 队首的任务,执行完成后,立即执行 Microtask 队列中的所有任务。

await后的内容为宏任务,await下的内容为异步任务

  1. async function async1() {
  2. await async2() // 宏任务1 async2() **await后的内容为宏任务,await下的内容为异步任务**
  3. console.log('async1 end') // 微任务1 可以理解为.then里面的内容
  4. }
  5. async function async2() {
  6. console.log('async2 end') // 宏任务1
  7. }
  8. async1() // 宏任务1
  9. // async2 end async1 end

总结

  1. js解析方法时,将同步任务排队到执行栈中,异步任务排队到事件队列中。
  2. 事件队列分为:
  • 宏任务:setTimeout,setInterval,setImmediate,I/O,UI交互事件
  • 微任务:process.nextTick,Promise.then
  1. 浏览器环境中执行方法时,先将执行栈中的任务清空,再将微任务推到执行栈中并清空,之后检查是否存在宏任务,若存在则取出一个宏任务,执行完成检查是否有微任务,以此循环…