Consider the following polyfill for queueMicrotask
.
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}
The description on MDN states.
It creates a microtask by using a promise that resolves immediately, falling back to using a timeout if the promise can't be created.
The queue-microtask library also uses the same polyfill. Here's what its documentation says.
- Optimal performance in all modern environments.
- Use
queueMicrotask
in modern environments (optimal)- Fallback to
Promise.resolve().then(fn)
in Node.js 10 and earlier, and old browsers (optimal)- Fallback to
setTimeout
in JS environments without Promise (slow)
This raises more questions than answers.
- Wouldn't
Promise
beundefined
in JS environments without promises? - Why are we throwing the error instead of calling
callback
withinsetTimeout
? - Why are we using a separate
catch
instead of passing the error handler tothen
? - How does this polyfill fallback to using
setTimeout
when “the promise can't be created”? - When would the promise not be created?
I would have expected the polyfill to be implemented as follows.
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = callback =>
typeof Promise === "function" && typeof Promise.resolve === "function"
? Promise.resolve().then(callback)
: setTimeout(callback, 0);
}
What's the reason why it's not implemented so?
Edit: I was going through the commit history of the queue-microtask library and I found 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))
So, it seems as though this library did indeed fallback to using cb => setTimeout(cb, 0)
. However, this was later removed. It might have been a mistake which went unnoticed. As for the MDN article, they might have just copied the snippet blindly from this library.