使用 queueMicrotask 来执行微任务

2019-09-09 admin

写在前面

写这篇文章的原因是因为,这几天在看 core-js 的yabo88狗亚app,然后发现了 queueMicrotask实现。由于之前做的项目,对于微任务的执行需求,一般是使用 asap 这个库来完成的,如果没有使用这个库的话,简易版本可以通过 Promise.resolve() 来代替,并没有接触过过这个 api,所以就抽时间研究一下。

兼容性

一般看这种偏 web 标准的新的 api,肯定上来要先看兼容性的,我去 caniuse 查了一下,wtf? 竟然搜索无结果。(详见 issue

然后只能去 MDN 来看一下了,大概是下图这个样子:

clipboard.png

可以发现还是比较新的 api,如果要在项目中直接使用的话,还是建议导入 polyfill 或者使用 asap 这个库来实现类似的需求。

为什么我们需要这个 api

从微任务本身的概念来说的话,就是当我们期望某段代码,不阻塞当前执行的同步代码,同时又期望它尽可能快地执行时,我们就需要它(这里不再赘述微任务的概念,可以参考这篇文章)。

一般情况下,如果是编写业务代码,我觉的很少会遇到这样的需求,唯一能想到的情况可能存在于一些对即时反馈有性能要求的场景,比如搜索,当输入关键字后发送异步请求获取搜索信息之后,我们可能会在前端对搜索结果进行一些处理,比如排序或者分组,但是这些操作可能不是优先级最高的任务,但它们又比较耗时(比如排序),因此我们可能期望推迟它们的执行,但又期望它们尽可能早地执行。

在阅读一些着名框架或者工具库的过程中,我发现很多情况下作者都会遇到这个需求,一般都通过 process.nextTick 或者 Promise.resolve 来解决。

它和 setTimeout 的区别?

本质上的区别应该在它们的执行时机上,而执行时机上的区别,本质上就是微任务和宏任务的区别。可以直接打开控制台运行一下以下的代码:

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 的一些观点来进行阐述:

  • 我们应当使用底层 api 来直接完成类似的功能,而非用顶层 api 进行模拟

  • 模拟过程中,对于异常情况,会造成一些困扰,比如 Promise.resolve 会将异常转化为一个 rejectedPromise

  • 模拟过程中,会创建额外的对象(造成一定意义上的浪费),比如 Promise.resolve 会返回一个 Promise 实例对象,而直接 queueMicrotask 则不会

  • 除了微任务,其他类型的异步任务都有对应的 api 可供使用,比如宏任务、RAF

  • 继上一点的基础上,语义性会更好,同时帮助开发者理解这些不同异步任务之间的区别

    • setTimeout(callback, 0) - 宏任务
    • requestAnimationFrame(callback) - RAF
    • queueMicrotask(callback) - 微任务

潜在问题

由于它是一个用于指派微任务的底层 api,我们很可能会在其中无限制地指派微任务到其队列之中,这样做的效果就是,浏览器的微任务队列始终处于非空状态,这将导致控制权始终无法交还给浏览器进行下一次事件循环,然后它就卡死了。

你可以执行下面的代码来体验这个现象:

function infiniteEnqueue(fn) {
    queueMicrotask(() => infiniteEnqueue(fn))
}

infiniteEnqueue(()=>{})

执行这段代码会使浏览器当前的 tab 卡死,请慎用,建议先打开浏览器提供的进程管理窗口以供强制关闭卡死窗口。

关于 polyfill 的不同实现

这里简单阐述 MDN 上的和 core-js 中的模拟方案。

MDN 上的 polyfill 实现比较简单粗暴,其实和直接调用 Promise.resolve 没什么区别,只是会在 .catch 中捕获错误之后再抛出。

相比较 MDN 的实现,core-js 会复杂一些,它同时考虑了 nodejsbrowser 两种情况,同时利用链表数据结构来模拟微任务队列的执行单元,同时实现了一个 flush 方法表示执行全部的微任务单元。

还实现了一个 notify 方法,该方法会根据具体的 js 运行时环境以及 api 的支持情况,分别尝试使用 process.nextTickMutationObserverPromise.resolve 以及最基本的宏任务 api 来执行 flush 方法,变相模拟微任务的执行过程。

参考


关注公众号 全栈_101,只谈技术,不谈人生。

clipboard.png


另:本人最近比较缺钱,业余时间接手各种规模的全栈外包项目或者开发任务,有意者私聊。

[转载]原文链接:https://segmentfault.com/a/1190000020332724

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-74701.html

文章标题:使用 queueMicrotask 来执行微任务

相关文章
jQuery中DOM树操作之使用反向插入方法实例分析
本文实例讲述了jQuery中DOM树操作之使用反向插入方法。分享给大家供大家参考。具体分析如下: 使用反向插入方法 这里我们先把创建的内容插人到元素前面,然后再把同一个元素插人到文档 中的另一个位置。通常,当在jQuery中操作元素时,利用...
2015-11-13
Bootstrap BootstrapDialog使用详解
这里有两种展现方式 写在前面:首先你要引入的库有 css : bootstrap.min.css bootstrap-dialog.css js : jquery-1.11.1.min.js bootstrap.min.js bootstr...
2017-03-16
vue-awesome-swiper的使用以及API整理
一、先说一个看关于vue-awesome-swiper的一个坑 vue项目的package.json中显示的<span style=“color: orange;”>“vue-awesome-swiper”: “^2.5.4”&...
2018-04-26
H5即将迎来黄金时代 轻应用再成行业焦点
鎽樿?侊细浠庣伀阃熻交搴旗敤銮峰缑鎶曡祫绛変腑涓嶉毦鐪嫔嚭锛孒TML5鍗冲皢杩庢潵榛勯噾镞朵唬銆傝秺鏉ヨ秺澶氱殑浼佷笟鎴栧垱涓氲€呭紑濮嬫秹瓒矵5锛岃?╄交搴旗敤鍐嶆?℃垚涓鸿?屼笟镄勭剑镣癸紝鎺ヤ笅鏉ュ皢链夋洿澶欻5寮曟搸浠ュ强镟村?欻5...
2015-11-12
[翻译]基于Webpack4使用懒加载分离打包React代码
原文地址:https://engineering.innovid.com/code-splitting-using-lazy-loading-with-react-redux-typescript-and-webpack-4-3ec601...
2018-03-11
在 Microsoft Azure 中使用 MEAN 堆栈基于开放数据协议
网络开发人员通常构建伟大的应用程序在客户端使用JavaScript和ASP(c#或Visual Basic . NET)在服务器端。 但是如果你能使用一个共同的语言来构建应用程序的所有层堆栈,从浏览器和服务器端业务处理服务层,甚至在数据库查...
2015-11-12
使用axios发送post请求,body传送数据格式form和json区别
先来看看这两个种传送格式的写法 1.form格式,将Content-Type类型设置为application/x-www-form-urlencode,POST请求时将data序列化,提交的数据会按照 key1=val1&key2=...
2018-07-25
Bootstrap table简单使用总结
最近接触一个很棒的插件,Bootstrap table没做过前端的表示对table的印象还只停留在html的table标签那一套,用过bootstrap table之后不得不说真的很实用。 构造方式 1 、HTML <div clas...
2017-03-17
微信公众号实现无限制推送模板消息!可向指定openID群发
微信认证的服务号才有推送模板消息接口 所以本文需要在认证服务号的情况下学习 以上就是模板消息,只有文字和跳转链接,没有封面图。 在服务号的后台添加功能插件-模板消息即可。 模板消息,都是在后台选择一个群发模板的,然后获取模板ID,根据这...
2018-01-14
游戏机遇到来 非主流HTML5能否成器?
2014年第四季度以来,有60%的游戏行业从业者已经投入或准备投入开发HTML5为基础的游戏,并对其前景表示乐观的态度。   对HTML 5这项诞生于2008年的标准来说,其正在经历最好的时代。 游戏机遇到来 非主流HTML5能否成器?  ...
2015-11-12
回到顶部