以下queueMicrotask polyfill如何回退到使用setTimeout?

问题描述 投票:1回答:1

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
  • 当“无法创建承诺”时,该polyfill如何回退到使用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文章,他们可能只是从该库中盲目复制了代码段。

javascript promise settimeout es6-promise event-loop
1个回答
0
投票

您的所有观点都完全正确,此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 );
}
© www.soinside.com 2019 - 2024. All rights reserved.