JS里的宏任务和微任务,我有点麻了
有这样一道题
1 | <script> |
问打印顺序?
说实话,刚看到的时候我确实有点麻,这不是纯属为了出题而出题嘛,每次遇到这种题的时候,我都是拒绝的,实际业务又不会遇到。但是仔细研究了下,一道题让我稍微理解了eventloop
promise
async/await
的执行方式,还是很值得的,在此记录如下。
什么是宏任务和微任务
我们都知道,js是一门单线程的语言,也就是说只有一条通道用来执行代码中的多个任务,代码在同步执行的时候遇到异步操作会放入队列里,等待主线程任务执行完后再从队列中拿出任务执行。队列里挂起的各种任务,就是宏任务(macrotask)和微任务(microtask)。宏任务和微任务分处于宏任务队列和微任务队列中。
除此之外,script,也就是最开始执行的同步代码,也是宏任务。 在浏览器里,常见的宏任务和微任务如下:
宏任务:script
,setTimeout/setInterval
,postMessage
,MessageChannel
微任务:Promise
执行机制
宏任务和微任务具体是怎么执行的呢?
js主线程先执行同步代码(同步代码就是上文说的script,其实也是一个宏任务)
执行过程中,遇到宏任务,放入宏任务队列,遇到微任务,放入微任务队列
同步代码执行完后,从微任务队列中获取微任务放入主线程执行
期间如果遇到微任务,将其放入微任务队列,再从微任务队列中获取任务执行
当前为任务队列为空后,才从宏任务队列中获取任务执行 执行宏任务
重复以上步骤,就完成了eventloop,也就是常说的事件循环
promise和async/await
ES6中javascript新增了promise对象,用于实现异步操作,promise意为承诺,我的理解是它包含了一个在未来才会返回值的事件(一般是异步操作),不管对错结果,它承诺会返回值。promise包含三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败) 并且状态只会根据异步操作的结果而改变。promise详解可以看阮一峰老师的promise教程
在这里我们只需要知道,一个promise实例存在then方法和catch方法,而这两种方法都是微任务
async/await是es7新特性,如果说promise的出现是为了解决回调地狱,那async/await也是为了解决promise的这一问题,当项目中有大量互相依赖的异步请求的时候,就会出现大量的.then,不仅可读性差,也影响代码优雅(hhh)
async/await详解推荐https://es6.ruanyifeng.com/#docs/async
咱们直接说重点 async/await能让异步操作看起来像是同步代码,并且async的本质其实是返回一个立即resolved的promise函数,await会阻塞后面的代码执行,但是他会先执行右边表达式,等有返回值了再返回
题目答案
根据以上描述,相信大家也能推出来了,答案就是:
1 | //script start |
首先代码同步执行,打印script start
遇到setTimeout,把它放入宏任务队列
然后执行async1,打印async1 start
遇到await,先执行右边表达式,打印async2
后面代码被阻塞,跳出回到同步代码,执行new Promise
Promise构造函数中的代码其实是同步代码,打印promise1
执行到.then发现是微任务,放入微任务队列
同步执行下面代码,打印script end
同步代码执行完毕,回到被await阻塞的代码
打印async2d的返回值async2 return
打印async1 end 代码执行完,从微任务队列中读取任务执行,也就是执行promise.then方法,打印promise2
至此当前宏任务执行完毕,从当前宏任务队列中读取任务执行,开始下一个事件循环 打印settimeout