Вы совершенно правы в своих основных моментах, этот полифилл не будет работать, если в среде нет Promise, и я действительно отредактировал статью MDN, чтобы теперь называть ее «обезьяньим патчем», так как он есть. и я удалил ссылку на «запасной вариант», поскольку нет.
Чтобы ответить на ваши вопросы:
- Да
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') );
Но эта «прокладка», по-видимому,
предназначена только для «современных двигателей» .
Редактор MDN, который сделал введя здесь исключение, выбрасывающее , сделало это, потому что спецификации спрашивают, что queueMicroTask
сообщает о любом исключении , которое будет выброшено во время выполнения callback
. Цепочка Promise будет " проглатывать " это исключение (оно не будет выброшено глобально), поэтому чтобы выйти из этой цепочки Promise, мы должны вызвать setTimeout
изнутри обработчика .catch()
.
Обработка со вторым параметром then
не будет обрабатывать исключения, возникающие при выполнении обратного вызова, и это именно то, что мы хотим сделать здесь.
Он не отступает ни к чему другому, кроме Promise
, как мы показали в предыдущих пунктах, он просто выбрасывал, если Promise
не определен, а setTimeout
используется только для выброса исключения из Цепочка обещаний.
Обещание не будет создано Promise.resolve()
, если эта функция будет чем-то иным, чем правильная реализация Promise. И если это так, то нет никаких шансов, что он вернет также и подхватываемый объект;) Но, как вы уже могли заметить, только текст объяснения был полностью введен в заблуждение.
Теперь, заметка о вашем обезьяньем патче, который все еще может быть немножко улучшен:
Этот редактор на самом деле был прав, сообщив об ошибке , catch
+ setTimeout
должен быть там.
queueMicrotask
должен выдать, если обратный вызов не Вызываемый .
Nitpick, но обратный вызов, переданный .then()
, будет вызываться с одним аргументом undefined
, queueMicrotask
вызывает его обратный вызов без каких-либо аргументов.
Снова Nitpick, проверяя каждый раз, если Promise доступен, звучит не очень хорошо, либо Promise определяется с самого начала, либо вы будете использовать полифил, который вы не знаете, как они управлял асинхронностью.
Что еще более важно (?), вы можете добавить поддержку большего количества сред.
* поставил в очередь микрозадачу * алгоритм 1090 * уже был частью веб-стандартов до того, как Promises попал в браузеры: 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' ) );
В node.js <0,11, <code>process.nextTick() был ближе всего к тому, что представляют собой микрозадачи, так что вы можете захотеть добавить его тоже (оно достаточно короткое).
if( typeof process === "object" && typeof process.nextTick === "function" ) {
process.nextTick( callback );
}
Таким образом, наш улучшенный полифилл будет выглядеть так:
(function() {
'use strict';
// lazy get globalThis, there might be better ways
const globalObj = typeof globalThis === "object" ? globalThis :
typeof global === "object" ? global :
typeof window === "object" ? window :
typeof self === 'object' ? self :
Function('return this')();
if (typeof queueMicrotask !== "function") {
const checkIsCallable = (callback) => {
if( typeof callback !== 'function' ) {
throw new TypeError( "Failed to execute 'queueMicrotask': the callback provided as parameter 1 is not a function" );
}
};
if( typeof Promise === "function" && typeof Promise.resolve === "function" ) {
globalObj.queueMicrotask = (callback) => {
checkIsCallable( callback );
Promise.resolve()
.then( () => callback() ) // call with no arguments
// if any error occurs during callback execution,
// throw it back to globalObj (using setTimeout to get out of Promise chain)
.catch( (err) => setTimeout( () => { throw err; } ) );
};
}
else if( typeof MutationObserver === 'function' ) {
globalObj.queueMicrotask = (callback) => {
checkIsCallable( callback );
const observer = new MutationObserver( function() {
callback();
observer.disconnect();
} );
const target = document.createElement( 'div' );
observer.observe( target, { attributes: true } );
target.setAttribute( 'data-foo', '');
};
}
else if( typeof process === "object" && typeof process.nextTick === "function" ) {
globalObj.queueMicrotask = (callback) => {
checkIsCallable( callback );
process.nextTick( callback );
};
}
else {
globalObj.queueMicrotask = (callback) => {
checkIsCallable( callback );
setTimeout( callback, 0 );
}
}
}
})();
queueMicrotask( () => console.log( 'microtask' ) );
console.log( 'sync' );