宏任务 & 微任务
JS 把异步任务分为宏任务和微任务。ES5 之后,JavaScript 引入 Promise,不需要浏览器,JavaScript 引擎自身也可以发起异步任务了。
宏任务由宿主(浏览器,Node)发起
宏任务包括 script,事件,网络请求(AJAX/Fetch),setTimeout,setInterval,setImmediate,I/O、UI 交互事件由宿主发起。
微任务由 JS 引擎发起。
Promise.then, Promise.catch, Promise.finally, MutationObserver 是微任务,由 JS 引擎发起。
注意:Promise 本身是同步的,但是其 then/catch 的回调是异步的。
宏任务、微任务执行过程
- 同步代码(js 执行/回调栈)
- 微任务的异步代码(js 引擎)
promise.nextTick(node)
Promise.then() Promise.catch()
Async/Await
Object.observe 等等 - 宏任务的异步代码(宿主环境)
script(代码块)
setTimeout,setInterval,setImmediate
一次事件循环 = (同步代码 + 微任务队列 + 宏任务队列) 放入执行栈,先进先出,执行完成。
举例
1. 执行顺序
1 | console.log("a"); |
输出结果:
1 | a |
2. 执行顺序
1 | Promise.resolve().then(() => { |
输出结果:
1 | Promise1 |
最后输出顺序为:Promise1 => setTimeout1 => Promise2 => setTimeout2。具体流程如下:
同步任务执行完毕。微任务 1 进入微任务队列,宏任务 1 进入宏任务队列。
查看微任务队列,微任务 1 执行,打印 Promise1,生成宏任务 2,进入宏任务队列。
查看宏任务队列,宏任务 1 执行,打印 setTimeout1,生成微任务 2,进入微任务队列。
查看微任务队列,微任务 2 执行,打印 Promise2。
查看宏任务队列,宏任务 2 执行,打印 setTimeout2。
3. 执行顺序
1 | const p = new Promise((resolve) => { |
输出顺序:
1 | 0 |
4. 执行顺序
1 | async function async1() { |
输出顺序:
1 | script start |
5. 执行任务
1 | async function async1() { |
输出顺序
1 | illegalscript start |
6. 执行任务
1 | new Promise((resolve, reject) => { |
输出结果
1 | 3; |
7. 执行任务
1 | setTimeout(() => { |
输出结果
1 | 2 |
EventLoop
- JS 是单线程的,同一个时间只能做一件事情
- 同步代码:立即放入 JS 引擎(JS 主线程)执行,并原地等待结果。
- 异步代码:先放入宿主环境(浏览器/Node),不必原地等待结果,并不阻塞主线程继续执行,异步结果在将来执行。异步任务等待时机到了以后把代码(回调函数)交给任务队列去排队执行。
- 每次事件循环就是执行栈里面代码执行完毕后,去查看任务队列里面是否还有其他任务需要执行,如果有,就按顺序依次执行。