Введение
Promise представляет собой конечный результат асинхронной операции. Основной способ взаимодействия с обещанием — через его метод then, который регистрирует обратные вызовы для получения либо конечного значения обещания, либо причины, по которой обещание не может быть выполнено.
Эта спецификация детализирует поведение метода then, предоставляя интероперабельную основу, от которой могут зависеть все реализации обещаний, совместимые с Promises/A+. Таким образом, спецификацию следует считать очень стабильной. Хотя организация Promises/A+ может время от времени пересматривать эту спецификацию, внося незначительные обратно совместимые изменения для решения недавно обнаруженных крайних случаев, мы будем интегрировать большие или обратно несовместимые изменения только после тщательного рассмотрения, обсуждения и тестирования.
Исторически сложилось так, что Promises/A+ разъясняет поведенческие пункты более раннего предложения Promises/A, расширяя его, чтобы охватить поведение де-факто и опуская части, которые недоопределены или проблематичны.
Наконец, базовая спецификация Promises/A+ не имеет отношения к тому, как создавать, выполнять или отклонять обещания, а вместо этого фокусируется на обеспечении интероперабельного метода then. Будущая работа в сопутствующих спецификациях может затронуть эти вопросы.
1. Терминология
promise — это объект или функция с методом then, поведение которых соответствует этой спецификации.
thenable — это объект или функция, определяющая метод then.
value — любое допустимое значение JavaScript (включая undefined, theable или promise).
exception — это значение, которое выбрасывается с помощью инструкции throw.
reason — это значение, указывающее, почему обещание было отклонено.
2. Требования
2.1 Состояния promise
#Promise должен находиться в одном из трех состояний: ожидание, выполнено или отклонено.
- В ожидании обещания:
может перейти либо в выполненное, либо в отклоненное состояние. - При выполнении обещание:
не должен переходить в какое-либо другое состояние.
должно иметь значение, которое не должно изменяться. - При отказе обещание:
не должен переходить в какое-либо другое состояние.
должна иметь причину, которая не должна меняться.
Здесь «не должен изменяться» означает неизменную идентичность (т. е. === ), но не подразумевает глубокую неизменность.
2.2 then - метод
Promise должен предоставлять метод#then для доступа к его текущему или возможному значению или причине.
Метод then обещания принимает два аргумента:
promise.then(onFulfilled, onRejected);2.2.1 И onFulfilled, и onRejected являются необязательными аргументами:
- Если onFulfilled не является функцией, ее нужно игнорировать.
- Если onRejected не является функцией, ее нужно игнорировать.
2.2.2 Если onFulfilled является функцией:
_ он должен вызываться после выполнения обещания со значением обещания в качестве первого аргумента.
_ его нельзя вызывать до того, как обещание выполнено.
* его нельзя вызывать более одного раза.
2.2.3 Если onRejected является функцией,
* он должен быть вызван после того, как обещание отклонено, с причиной обещания в качестве первого аргумента.
* его нельзя вызывать до того, как обещание будет отклонено.
* его нельзя вызывать более одного раза.
2.2.4 onFulfilled или onRejected не должны вызываться до тех пор, пока стек контекста выполнения не будет содержать только код платформы. [3.1].
2.2.5 onFulfilled и onRejected должны вызываться как функции (т.е. без значения this). [3.2]
2.2.6 then может вызываться несколько раз для одного и того же промиса.
-
Если/когда обещание выполнено, все соответствующие обратные вызовы onFulfilled должны выполняться в порядке их исходящих вызовов then.
-
Если/когда обещание отклонено, все соответствующие обратные вызовы onRejected должны выполняться в порядке исходных вызовов then.
2.2.7 then должен вернуть обещание [3.3].
promise2 = promise1.then(onFulfilled, onRejected);- Если onFulfilled или onRejected возвращает значение x, запустите процедуру разрешения промисов Resolve(promise2, x).
- Если либо onFulfilled, либо onRejected выдает исключение e, обещание2 должно быть отклонено с указанием e в качестве причины.
- Если onFulfilled не является функцией и обещание1 выполнено, обещание2 должно быть выполнено с тем же значением, что и обещание1.
- Если onRejected не является функцией, а обещание1 отклонено, обещание2 должно быть отклонено по той же причине, что и обещание1.
2.2.3 Процедура разрешения обещаний
Процедура разрешения промисов — это абстрактная операция, принимающая в качестве входных данных промис и значение, которое мы обозначаем как Resolve(promise, x). Если х является тоном, он пытается заставить обещание принять состояние х в предположении, что х ведет себя, по крайней мере, в некоторой степени как обещание. В противном случае он выполняет обещание со значением x.
Такое обращение с#thenable позволяет реализациям promise взаимодействовать, пока они предоставляют метод then, совместимый с Promises/A+. Это также позволяет реализациям Promises/A+ «ассимилировать» несовместимые реализации разумными тогда методами.
Чтобы запустить Resolve(promise, x), выполните следующие действия:
2.3.1 Если обещание и x ссылаются на один и тот же объект, отклонить обещание с ошибкой типа в качестве причины.
2.3.2 Если x — обещание, принять его состояние [3.4]:
-
Если x находится в ожидании, обещание должно оставаться в ожидании до тех пор, пока x не будет выполнено или отклонено.
-
Если/когда x выполнено, выполнить обещание с тем же значением.
-
Если/когда x отклонено, отклоните обещание по той же причине.
2.3.3 В противном случае, если x является объектом или функцией,
-
Пусть then будет х.then. [3.5]
-
Если получение свойства x.then приводит к возникновению исключения e, отклоните обещание с e в качестве причины.
-
Если then это функция, вызовите ее с x как this, первым аргументом resolvePromise и вторым аргументом rejectPromise, где: _ Если/когда вызывается resolvePromise со значением y, запустите Resolve(promise, y).
_ Если/когда rejectPromise вызывается с причиной r, отклонить обещание с r.
_ Если вызываются и resolvePromise, и rejectPromise, или выполняется несколько вызовов одного и того же аргумента, первый вызов имеет приоритет, а любые последующие вызовы игнорируются.
_ Если вызов вызывает исключение e, _ Если были вызваны resolvePromise или rejectPromise, игнорируйте их.
_ В противном случае отклоните обещание с e в качестве причины. * Если then не является функцией, выполнить обещание с x.2.3.4 Если x не является объектом или функцией, выполнить обещание с помощью x.
Если промис разрешается с помощью#thenable, который участвует в циклической цепочке thenable, так что рекурсивный характер Resolve(promise, thenable) в конечном итоге приводит к повторному вызову Resolve(promise, thenable) после приведенный выше алгоритм приведет к бесконечной рекурсии. Реализациям рекомендуется, но не обязательно обнаруживать такую рекурсию и отклонять обещания с информативной ошибкой типа в качестве причины. [3.6]
3. Примечание
3.1 Здесь «код платформы» означает движок, среду и код реализации промисов. На практике это требование гарантирует, что onFulfilled и onRejected будут выполняться асинхронно, после поворота цикла событий, в котором затем вызывается then, и с новым стеком. Это может быть реализовано либо с помощью механизма «макрозадачи», такого как setTimeout или setImmediate, либо с помощью механизма «микрозадачи», такого как MutationObserver или process.nextTick. Поскольку реализация промиса считается кодом платформы, она может сама содержать очередь планирования задач или «трамплин», в котором вызываются обработчики.
3.2 То есть в строгом режиме this внутри них будет неопределенным; в небрежном режиме это будет глобальный объект.
3.3 Реализации могут разрешать promise2 = promise1 при условии, что реализация соответствует всем требованиям. Каждая реализация должна документировать, может ли она создать promise2 = promise1 и при каких условиях.
3.4 Как правило, будет известно, что x является истинным обещанием только в том случае, если оно исходит из текущей реализации. Этот пункт позволяет использовать средства, специфичные для реализации, для принятия состояния известных совместимых обещаний.
3.5 Эта процедура, при которой сначала сохраняется ссылка на x.then, затем проверяется эта ссылка, а затем вызывается эта ссылка, позволяет избежать многократного доступа к свойству x.then. Такие меры предосторожности важны для обеспечения согласованности перед свойством доступа, значение которого может меняться между извлечениями.
3.6 Реализации не должны устанавливать произвольные ограничения на глубину цепочек thenable и предполагать, что за пределами этого произвольного ограничения рекурсия будет бесконечной. Только истинные циклы должны приводить к TypeError; если встречается бесконечная цепочка различных значений, правильным поведением будет рекурсия навсегда.