Aad*_*hah 3 javascript event-loop settimeout promise es6-promise
考虑以下polyfill for queueMicrotask.
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}
Run Code Online (Sandbox Code Playgroud)
MDN 上的描述说明。
它通过使用立即解决的承诺来创建微任务,如果无法创建承诺,则回退到使用超时。
该队列microtask库也使用相同的填充工具。这是它的文档所说的。
- 在所有现代环境中的最佳性能。
queueMicrotask在现代环境中使用(最佳)- 回退到
Promise.resolve().then(fn)Node.js 10 及更早版本,以及旧浏览器(最佳)setTimeout在没有 Promise 的 JS 环境中回退(慢)
这提出的问题多于答案。
Promise是undefined在JS环境,而无需承诺?callbackinside setTimeout?catch而不是将错误处理程序传递给then?setTimeout当“无法创建承诺”时,这个 polyfill 如何回退到使用?我本来希望 polyfill 实现如下。
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = callback =>
typeof Promise === "function" && typeof Promise.resolve === "function"
? Promise.resolve().then(callback)
: setTimeout(callback, 0);
}
Run Code Online (Sandbox Code Playgroud)
没有实施的原因是什么?
编辑:我正在浏览 queue-microtask 库的提交历史记录,我发现了这个 commit。
@@ -1,9 +1,8 @@
-let resolvedPromise
+let promise
module.exports = typeof queueMicrotask === 'function'
? queueMicrotask
- : (typeof Promise === 'function' ? (resolvedPromise = Promise.resolve()) : false)
- ? cb => resolvedPromise
- .then(cb)
- .catch(err => setTimeout(() => { throw err }, 0))
- : cb => setTimeout(cb, 0)
+ // reuse resolved promise, and allocate it lazily
+ : cb => (promise || (promise = Promise.resolve()))
+ .then(cb)
+ .catch(err => setTimeout(() => { throw err }, 0))
Run Code Online (Sandbox Code Playgroud)
因此,似乎这个库确实回退到使用cb => setTimeout(cb, 0). 不过,这后来被删除了。这可能是一个没有引起注意的错误。至于 MDN 文章,他们可能只是盲目地从这个库中复制了代码片段。
你的主要观点完全正确,如果环境中没有 Promise,这个 polyfill 将无法工作,我确实编辑了 MDN 文章,现在称它为“猴子补丁”,因为它就是这样,我删除了对“后备”的引用,因为没有。
回答您的问题:
Promise将是未定义的,因此 polyfill 只会抛出:delete window.queueMicrotask;
delete window.Promise;
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}
queueMicrotask( () => console.log('hello') );Run Code Online (Sandbox Code Playgroud)
确实在此处引入异常抛出的 MDN 编辑器之所以这样做,是因为规范要求queueMicroTask报告在callback执行期间会抛出的任何异常。Promise 链会“吞下”这个异常(它不会被全局抛出),所以为了摆脱这个 Promise 链,我们必须setTimeout从.catch()处理程序内部调用。
从第二个参数的then处理不会处理回调执行抛出的异常,这正是我们在这里想要做的。
除了 ,它不会回退到其他任何东西Promise,正如我们在前面的项目符号中所示,它只会在Promise未定义的情况下抛出,并且setTimeout仅用于将 Exception 抛出 Promise 链。
Promise.resolve()当该函数不是正确的 Promise 实现时,将不会创建Promise。如果是这种情况,它也没有机会返回可捕获的对象 ;) 但是正如您现在可能已经发现的那样,只有解释文本完全具有误导性。
现在,关于您的猴子补丁的说明仍然可能会有所改进:
这个编辑器实际上是正确的,应该报告错误,catch+setTimeout应该在那里。
queueMicrotask如果回调不是Callable ,则应该抛出。
鸡蛋里挑骨头,但传递给回调 .then()将用一个参数调用undefined,queueMicrotask调用其回调不带任何参数。
再次吹毛求疵,每次检查 Promise 是否可用听起来不太好,要么从一开始就定义了 Promise,要么你将使用一个你不知道他们如何管理异步性的 polyfill。
更重要的是 (?) 您可能希望添加对更多环境的支持。
在 Promises 进入浏览器之前,队列微任务算法已经是 Web 标准的一部分:MutationObserver 队列微任务也是IE11 支持(与 Promises 不同)。
function queueMutationObserverMicrotask( callback ) {
var observer = new MutationObserver( function() {
callback();
observer.disconnect();
} );
var target = document.createElement( 'div' );
observer.observe( target, { attributes: true } );
target.setAttribute( 'data-foo', '' );
}
Promise.resolve().then( () => console.log( 'Promise 1' ) );
queueMutationObserverMicrotask( () => console.log('from mutation') );
Promise.resolve().then( () => console.log( 'Promise 2' ) );Run Code Online (Sandbox Code Playgroud)
在 node.js < 0.11 中,process.nextTick()最接近微任务,因此您可能也想添加它(它足够短)。
if( typeof process === "object" && typeof process.nextTick === "function" ) {
process.nextTick( callback );
}
Run Code Online (Sandbox Code Playgroud)
总而言之,我们改进后的 polyfill 看起来像
if( typeof process === "object" && typeof process.nextTick === "function" ) {
process.nextTick( callback );
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1274 次 |
| 最近记录: |