Це якось круте запитання, до якого можна дійти.
Коли ви робите це:
verifier(3,4).then(...)
що повертає нову обіцянку, яка вимагає ще одного циклу назад до циклу подій, перш ніж ця нещодавно відхилена обіцянка зможе запустити .catch()обробник, який слід. Цей додатковий цикл дає наступну послідовність:
verifier(5,4).then(...)
шанс запустити свій .then()обробник до попереднього рядка, .catch()оскільки він уже був у черзі до того, як .catch()обробник з першого потрапить у чергу, і елементи запускаються з черги у порядку FIFO.
Зверніть увагу, що якщо ви використовуєте .then(f1, f2)форму замість .then().catch(), вона дійсно запускається тоді, коли ви цього очікуєте, оскільки немає додаткових обіцянок і, отже, жодної додаткової галочки:
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response (3,4): ", response),
(error) => console.log("error (3,4): ", error)
);
verifier(5, 4)
.then((response) => console.log("response (5,4): ", response))
.catch((error) => console.log("error (5,4): ", error));
Зверніть увагу, я також позначив усі повідомлення, щоб ви могли бачити, з якого verifier()дзвінка вони надходять, що полегшує читання результатів.
ES6 Специфікація щодо замовлення зворотного виклику та більш детальне пояснення
Специфікація ES6 повідомляє нам, що обіцяні "завдання" (як це викликає зворотний виклик з .then()або .catch()) виконуються у порядку FIFO на основі того, коли вони вставляються в чергу завдань. Він конкретно не називає FIFO, але вказує, що нові завдання вставляються в кінці черги, а завдання виконуються з початку черги. Це реалізує замовлення FIFO.
PerformPromiseThen (який виконує зворотний виклик з .then()) призведе до EnqueueJob, тобто, як обробник вирішення або відхилення планується фактично запустити. EnqueueJob вказує, що очікуване завдання додається в зворотному боці черги завдань. Потім операція NextJob витягує елемент з передньої частини черги. Це забезпечує порядок FIFO при обслуговуванні завдань із черги завдань Promise.
Отже, у прикладі у вихідному питанні ми отримуємо зворотні виклики для verifier(3,4)обіцянки та verifier(5,4)обіцянки, вставлених у чергу завдань у тому порядку, в якому вони були запущені, оскільки обидві ці початкові обіцянки виконані. Потім, коли перекладач повертається до циклу подій, він спочатку бере verifier(3,4)роботу. Цю обіцянку відхилено, і для неї немає зворотного виклику verifier(3,4).then(...). Отже, що він робить, це відхиляє обіцянку, яка verifier(3,4).then(...)повернулася, і що призводить до того, що verifier(3,4).then(...).catch(...)обробник буде вставлений у jobQueue.
Потім він повертається до циклу подій, і наступним завданням, яке він витягує з jobQueue, є verifier(5, 4)завдання. Це має вирішену обіцянку та обробник вирішення, тому він викликає цей обробник. Це призводить до відображення response (5,4):вихідних даних.
Потім він повертається до циклу подій, і наступним завданням, яке він витягує з jobQueue, є verifier(3,4).then(...).catch(...)завдання, де воно виконується, і це призводить error (3,4)до відображення результату.
Це тому, що .catch()1-й ланцюжок - це один рівень обіцянки, глибший у своєму ланцюгу, ніж .then()2-й ланцюжок, що спричинює порядок, про який ви повідомили. І це тому, що ланцюжки обіцянок переходять від одного рівня до наступного через чергу завдань у порядку FIFO, а не синхронно.
Загальна рекомендація щодо покладання на цей рівень деталізації планування
FYI, загалом, я намагаюся писати код, який не залежить від цього рівня детальних знань часу. Хоча це цікаво і іноді корисно розуміти, це крихкий код, оскільки проста, здавалося б, нешкідлива зміна коду може призвести до зміни відносних термінів. Отже, якщо хронометраж є критичним між двома ланцюжками, як це, то я волів би писати код таким чином, щоб змусити хронометраж так, як я хочу, а не покладатися на цей рівень детального розуміння.