Ответ 1
Соответствующие спецификации здесь:
-
onFulfilled
илиonRejected
не нужно вызывать, пока стек контекста выполнения не содержит только код платформы. [3,1].И обратите внимание на 3.1 (акцент мой):
Здесь "код платформы" означает код реализации, среды и обещания. На практике это требование гарантирует, что
onFulfilled
иonRejected
выполняются асинхронно, после того, как цикл цикла обработки событий, который затем вызывается, и со свежим стекем. Это может быть реализовано либо с помощью механизма макрозадачи, такого какsetTimeout
илиsetImmediate
, либо с помощью механизма "микрозадания", такого какMutationObserver
илиprocess.nextTick
. Поскольку реализация обещаний считается кодом платформы, она может сама содержать очередь планирования задач или "батут", в котором вызываются обработчики. -
ECMAScript 6.0 (на основе Promises/A +) немного сложнее извлечь фрагменты, но
then
разрешает как в разделе 25.4.5.3.1:-
Иначе, если значение обещания [[PromiseState]] внутреннего слота
"fulfilled"
,а. Пусть значение будет значением внутреннего слота [[PromiseResult]].
б. Выполните EnqueueJob (
"PromiseJobs"
, PromiseReactionJob, "performReaction, value" ). -
Иначе, если значение обещания [[PromiseState]] внутреннего слота
"rejected"
,а. Пусть причина - это стоимость обещания [[PromiseResult]] внутреннего слота.
б. Выполните EnqueueJob (
"PromiseJobs"
, PromiseReactionJob, "rejectReaction, reason" ).
-
И важная операция EnqueueJob определена в разделе раздел 8.4 ( "Задания и очереди работы" ), показывая это в своем предисловии (жирным шрифтом является мой):
Выполнение задания может быть начато только в том случае, если не существует исполняемого контекста выполнения, а стек контекста выполнения пуст. [...] После запуска задания выполняется задание всегда выполняется до завершения. Ни одно другое задание не может быть начато до завершения текущего выполняемого задания.
На практике это позволяет сделать несколько простых и последовательных утверждений:
- Вы можете рассчитывать на
then
илиcatch
(и т.д.), чтобы всегда вести себя асинхронно, никогда не синхронно. - Вы никогда не увидите нескольких обработчиков
then
илиcatch
в одном стеке, даже если одно обещание явно разрешено в рамках другого обещания. Это также означает, что рекурсивное выполнение Promise не рискует переполнением стека, поскольку может случиться обычный вызов функции, хотя вы все равно можете столкнуться с кучей пространства, если вы небрежны с рекурсивными замыканиями в патологическом случае. - Многопользовательские операции, поставленные в очередь в обработчике
then
илиcatch
, никогда не будут блокировать текущий поток, даже если обещание уже установлено, поэтому вы можете поставить в очередь ряд асинхронных операций, не беспокоясь о порядке или обещании состояние. - В
then
илиcatch
никогда не будет включенtry
, даже при вызовеthen
на уже установленном Promise, поэтому нет никакой двусмысленности в отношении того, должна ли платформа обрабатывать возникшее исключение.