为polyfill考虑以下queueMicrotask
。
queueMicrotask
关于MDN状态的描述。
[它使用立即解决的诺言创建微任务,如果无法创建诺言,则退回到使用超时。
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}
库也使用相同的polyfill。这是其文档所说的内容。
- 在所有现代环境中均具有最佳性能。
- 在现代环境中使用queue-microtask(最佳)
- 在Node.js 10及更低版本中,返回到
queueMicrotask
,以及旧的浏览器(最佳)- 在没有承诺的JS环境中回退到
Promise.resolve().then(fn)
(缓慢)
这提出的问题多于答案。
setTimeout
在没有承诺的JS环境中是否会成为Promise
?undefined
中调用callback
?setTimeout
而不是将错误处理程序传递给catch
?then
?我希望该polyfill可以如下实现。
setTimeout
为什么不实施它的原因是什么?
编辑:我正在研究队列微任务库的提交历史,并且发现了if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = callback =>
typeof Promise === "function" && typeof Promise.resolve === "function"
? Promise.resolve().then(callback)
: setTimeout(callback, 0);
}
。
this 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))
。但是,此内容后来被删除。这可能是一个未被注意到的错误。至于MDN文章,他们可能只是从该库中盲目复制了代码段。
您的所有观点都完全正确,此polyfill无法正常工作。
cb => setTimeout(cb, 0)
是不确定的,因此polyfill只会抛出:Promise
仅在回调不是函数时才能在此处抛出任何内容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') );
都没有意义。
这里的捕获完全没有用,queueMicrotask不应处理任何错误。 (请注意,使用单独的queueMicrotask
可以在执行.catch()
时捕获错误,而callback
则会丢失)。
它没有回退到.then( res, err )
以外的任何地方,正如我们在第一个项目符号中所示,如果未定义Promise
,它只会抛出该错误。
如果Promise实现不是正确的Promise实现,则不会由Promise
创建Promise。如果是这种情况,它也几乎不可能返回可捕获的对象;)
现在,关于您的版本的说明,确实是更好的猴子补丁,但可能仍会有所改进:
[Promise.resolve()
应该在callback不是Callable时抛出。
Nitpick,但是传递给queueMicrotask
的回调将使用一个参数.then()
进行调用,undefined
调用不带任何参数的回调。
再次使用Nitpick,每次检查Promise是否可用听起来都不太好,或者从一开始就定义了Promise,或者您将使用一个polyfill,而您不知道他们如何管理异步性。
更重要的是(?),您可能想添加对更多环境的支持。
queueMicrotask
算法在Promises进入浏览器之前已经是Web标准的一部分:queue a microtask MutationObserver
,并且在IE11中受支持(与Promises不同)。
在node.js <0.11中,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' ) );
最接近微任务,因此您可能也想添加它(足够简短)。
process.nextTick()
总的来说,我们改进的polyfill看起来像
if( typeof process === "object" && typeof process.nextTick === "function" ) {
process.nextTick( callback );
}