什么叫事件循环?
简单来说就是:主线程执行完同步任务后,依次执行异步任务的微任务队列和宏任务队列,这个过程是循环不断的,整个的这种运行机制称为事件循环(EventLoop)。
微任务(micro task):process.nextTick、await、promise.then
宏任务(macro task):setTimeout、setInterval、setImmediate
微任务、宏任务的区别
执行完同步任务后,会有异步任务的微任务队列和宏任务队列。微任务先执行,然后是宏任务。
1 | setTimeout(() => { |
Promise 以及链式调用 then 的执行方式
Promise 的 executor 是一个同步函数,立即执行的函数,因此它应该是和当前的任务一起同步执行的。而 Promise 的链式调用 then,每次都会在内部生成一个新的 Promise,然后执行 then,在执行的过程中不断向微任务队列推入新的任务,因此直至微任务队列清空后才会执行宏任务。
1 | setTimeout(() => { |
链式调用 then 每次都生成一个新的 Promise,也就是说每个 then 的回调方法属于同一个微任务队列。
嵌套式的 Promise
遇到嵌套式的 Promise 不要慌,首先要心中有一个队列,能够将这些任务放到相对应的队列之中。
在 then 的回调函数中创建一个新的 Promise
1 | new Promise((resolve, reject) => { |
第一轮
- current task:Promise 的 executor 立即执行,最先输出
promise1
- micro task queue:
[then11]
第二轮
- current task:执行 then11 的回调函数,输出
then11
,紧接着第二个 Promise 的 executor 立即执行,输出promise2
- micro task queue:
[then21, then12]
第三轮
- current task:执行 then21 的回调函数,输出
then21
,执行 then12 的回调函数,输出then12
- micro task queue:
[then22]
第三轮
- current task:执行 then22 的回调函数,输出
then22
链式调用 then 可能会被 Evenloop 中其他的任务插队:then12 的回调函数 在 then22 之前执行,若改为分别调用 then 则不会被插队
在 then 的回调函数中返回一个新的 Promise
1 | new Promise((resolve, reject) => { |
这里的 then12 相当于是挂在新 Promise 的最后一个 then 的返回值上。
多个 Promise 的情况
1 | new Promise((resolve, reject) => { |
第一轮
- current task:输出
promise1
,promise3
- micro task queue:
[then11, then3]
第二轮
- current task:执行 then11 的回调函数,输出
then11
,输出promise2
,执行 then3 的回调函数,输出then3
- micro task queue:
[then21, then12]
第三轮
- current task:执行 then21 的回调函数,输出
then21
,执行 then12 的回调函数,输出then12
- micro task queue:
[then22]
第三轮
- current task:执行 then22 的回调函数,输出
then22
async/await 对 Eventloop 的影响
async/await 仅仅影响的是函数内的执行,而不会影响到函数体外的执行顺序。
1 | async function async1() { |
await async2()
相当于一个 Promise,后面的语句相当于 then 的回调函数
NodeJS 事件 + Promise + async/await + setImmediate
1 | async function async1() { |
第一轮
- current task:输出
script start
,async1 start
,async2
,promise
,script end
- micro task queue:
[await, then, nextTick]
- macro task queue:
[setTimeout, setImmediate]
第二轮
- current task:输出
process
,async1 end
,then
,setTimeout
,setImmediate
小结
在处理一段 EvenLoop 执行顺序的时候:
- 第一:确定微任务,宏任务
- 第二:解析“拦路虎”,出现 async/await 不要慌,它们只在标记的函数中能够作威作福,出了这个函数还是跟着大部队的潮流。
- 第三:根据 Promise 中 then 的调用方式不同做出不同的判断,是链式还是分别调用。
- 最后:优先级:
process.nextTick
>await
>promise.then