Раздел 25.6.5.4.1 спецификации ECMA определяет поведение Promise.prototype.then
s как:
[...]
Если обещание. [[PromiseState]] «ожидает», «ожидает», тогда
a.Добавьте executeReaction в качестве последнего элемента списка, который является обещанием. [[PromiseFulfillReactions]].
b.Добавьте rejectReaction в качестве последнего элемента списка, который является обещанием. [[PromiseRejectReactions]].
Иначе, если обещание. [[PromiseState]] "выполнено" "выполнено", затем
а.Пусть значение будет обещанием. [[PromiseResult]].
b.Выполнить EnqueueJob («PromiseJobs», «PromiseJobs», PromiseReactionJob, «executeReaction, value»).
Так что если вы присоединяете .then
обратных вызовов к Promise (с именем «fullfillReaction»и "rejectReaction" здесь), и обещание еще не разрешено, обратные вызовы будут сохранены для последующего вызова (7a и b).
Если Обещание уже разрешено к тому времени (8), то оно непосредственно создаст задание для обратного вызова обратного вызова (8b), которое будет выполнено практически сразу (при следующей микротике).
не слишком ли поздно регистрироваться .then с нашей функцией завершения?
Нет, еще не слишком поздно.Обещание все еще знает значение, к которому оно разрешено, и будет вызывать обратный вызов .then
с этим почти мгновенно.
Например:
const promise = new Promise((resolve) => { // A promise gets created and is in "pending" state
setTimeout(() => {
resolve("value"); // the promise gets turned into "fullfilled" state, and "value" will be stored in the internal PromiseResult property of the promise
}, 1000);
});
// By now the promise did not resolve yet, "callback" will be stored internally
promise.then(function callback() { /*...*/ });
// one second later, the promise fullfills and "callback" gets called. The promise is in "fullfilled" state
setTimeout(() => { // another second later, this runs
// As the promise is fullfilled already, "callback2" will directly put into a job onto the promise microtask queue
promise.then(function callback2() {
console.log("callback2 called");
});
// ...then the line below will run
console.log("then attached after 2s");
// at this point, synchronous code finishes, the call stack unwinds
// the engine will continue working on the microtask queue
// and voíla, it'll find the "callback2" callback, and will call it with the promises result (so it gets executed "nearly immeadiately")
}, 2000);
¹: Это простогарантировать, что .then(...)
всегда будет работать "асинхронно".Для простоты можно сказать, что функция запускается напрямую.