写这篇文章的缘由是由于,这几天在看 core-js
的源码,而后发现了 queueMicrotask
的实现。因为以前作的项目,对于微任务的执行需求,通常是使用 asap
这个库来完成的,若是没有使用这个库的话,简易版本能够经过 Promise.resolve()
来代替,并无接触过这个 api
,因此就想着抽时间研究一下。前端
通常看这种偏 web 标准的新的 nodeapi
,确定上来要先看兼容性的,我去 caniuse
查了一下,wtf? 居然搜索无结果。(详见 issue)
而后只能去 MDN 来看一下了,大概是下图这个样子:git
昨天多是拼写错误吧,受网友指点,来补充一下 caniuse 上面的截图:github
能够发现仍是比较新的 api
,若是要在项目中直接使用的话,仍是建议导入 polyfill
或者使用 asap
这个库来实现相似的需求。web
api
?从微任务自己的概念来讲的话,就是当咱们指望某段代码,不阻塞当前执行的同步代码,同时又指望它尽量快地执行时,咱们就须要它(这里再也不赘述微任务的概念,能够参考这篇文章)。api
通常状况下,若是是编写业务代码,我觉的不多会遇到这样的需求,惟一能想到的状况可能存在于一些对即时反馈有性能要求的场景,好比搜索,当输入关键字后发送异步请求获取搜索信息以后,咱们可能会在前端对搜索结果进行一些处理,好比排序或者分组,可是这些操做可能不是优先级最高的任务,但它们又比较耗时(好比排序),所以咱们可能指望推迟它们的执行,但又指望它们尽量早地执行。浏览器
在阅读一些著名框架或者工具库的过程当中,我发现不少状况下做者都会遇到这个需求,通常都经过 process.nextTick
或者 Promise.resolve
来解决。bash
setTimeout
的区别?本质上的区别应该在它们的执行时机上,而执行时机上的区别,本质上就是微任务和宏任务的区别。能够直接打开控制台运行一下如下的代码:markdown
setTimeout(() => { console.log('setTimeout'); }, 0); queueMicrotask(() => { console.log('queueMicrotask'); }); 复制代码
运行结果不出意外应该是:数据结构
queueMicrotask setTimeout 复制代码
若是你熟悉 nodejs
的话,应该和 process.nextTick
是相似的。
这也是我一开始脑海中出现的问题,就是既然咱们已经能够经过别的方式来模拟微任务的执行,咱们还须要这个 api
干什么?好比,经过下面的代码:
setTimeout(() => { console.log('setTimeout'); }, 0); Promise.resolve().then(() => { console.log('queueMicrotask'); }); 复制代码
会获得和上面代码同样的运行结果。
这里引用 Explainer: queueMicrotask 的一些观点来进行阐述:
Promise.resolve
会将异常转化为一个 rejected
的 Promise
Promise.resolve
会返回一个 Promise
实例对象,而直接 queueMicrotask
则不会api
可供使用,好比宏任务、RAF
setTimeout(callback, 0)
- 宏任务requestAnimationFrame(callback)
- RAFqueueMicrotask(callback)
- 微任务因为它是一个用于指派微任务的底层 api
,咱们极可能会在其中无限制地指派微任务到其队列之中,这样作的效果就是,浏览器的微任务队列始终处于非空状态,这将致使控制权始终没法交还给浏览器进行下一次事件循环,而后它就卡死了。
你能够执行下面的代码来体验这个现象:
function infiniteEnqueue(fn) { queueMicrotask(() => infiniteEnqueue(fn)) } infiniteEnqueue(()=>{}) 复制代码
执行这段代码会使浏览器当前的 tab
卡死,请慎用,建议先打开浏览器提供的进程管理窗口以供强制关闭卡死窗口。
这里简单阐述 MDN
上的和 core-js
中的模拟方案。
MDN 上的 polyfill 实现比较简单粗暴,其实和直接调用 Promise.resolve
没什么区别,只是会在 .catch
中捕获错误以后再抛出。
相比较 MDN 的实现,core-js
会复杂一些,它同时考虑了 nodejs
和 browser
两种状况,同时利用链表数据结构来模拟微任务队列的执行单元,同时实现了一个 flush
方法表示执行所有的微任务单元。
还实现了一个 notify
方法,该方法会根据具体的 js
运行时环境以及 api
的支持状况,分别尝试使用 process.nextTick
、MutationObserver
和 Promise.resolve
以及最基本的宏任务 api
来执行 flush
方法,变相模拟微任务的执行过程。
关注公众号 全栈_101,只谈技术,不谈人生。
业余时间接手各类规模的外包项目,有意者私信。