Promises/A+
実装者による, 実装者のための, 信頼でき, 相互運用可能な, JavaScript ~promiseのための~open標準。 ◎ An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
序論
`~promise$は、非同期的~演算の最終的な結果を表現する。 ~promiseとのやりとりは、主に,その `then^es ~methodを通して~~行われる — それは、[ ~promiseの最終的な値を受取る~callback ], または/および[ ~promiseが履行できなかったときに その`事由$を受取る~callback ]を登録する。 ◎ A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.
この仕様は、 `then^es ~methodの挙動の詳細を与える — それは、[ すべての[ Promises/A+ に適合する~promise実装 ]が,それを提供することに依存できる ]ような,相互運用可能な基盤を提供する。 そのようなわけで、この仕様は ごく安定的と見なされるべきである。 新たに際どい事例が発見されれば,それに取り組むため、この仕様は Promises/A+ organization により[ 小さな, かつ後方互換な変更点 ]で改訂されることもあり得るが、[ 大きな, または後方互換でないもの ]が統合されるのは、[ 注意深く考察され, 議論が積み重ねられ, 試験された ]後に限られることになる。 ◎ This specification details the behavior of the then method, providing an interoperable base which all Promises/A+ conformant promise implementations can be depended on to provide. As such, the specification should be considered very stable. Although the Promises/A+ organization may occasionally revise this specification with minor backward-compatible changes to address newly-discovered corner cases, we will integrate large or backward-incompatible only after careful consideration, discussion, and testing.
歴史的に, Promises/A+ は、早期の Promises/A 提案 の挙動上の各~条項を明確化し、[ 事実的標準 とされている挙動を~coverする ]ために それを拡張し,また[ 指定中にある/問題ある ]部分は切落としてきた。 ◎ Historically, Promises/A+ clarifies the behavioral clauses of the earlier Promises/A proposal, extending it to cover de facto behaviors and omitting parts that are underspecified or problematic.
~~最終的に,中核の Promises/A+ 仕様は、どのように~promiseを 作成する/`履行する$/`却下する$ かについては 取り扱わず、代わりに,相互運用可能な `then^es ~methodを提供することに集中する。 姉妹~仕様における将来の成果が,これらの主題に触れることになるであろう。 ◎ Finally, the core Promises/A+ specification does not deal with how to create, fulfill, or reject promises, choosing instead to focus on providing an interoperable then method. Future work in companion specifications may touch on these subjects.
1. 用語集
- `~promise@
- `then^es ~methodを備える ~object/~function であって,その挙動がこの仕様に適合するもの。 ◎ “promise” is an object or function with a then method whose behavior conforms to this specification.
- `~thenable@
- `then^es ~methodを定義するような, ~object/~function 。 ◎ “thenable” is an object or function that defines a then method.
- `値@
- 任意の合法な JavaScript 値( `undefined^es, `~thenable$, `~promise$も含む)。 ◎ “value” is any legal JavaScript value (including undefined, a thenable, or a promise).
- `例外@
- `throw^c 文を利用して投出される`値$。 ◎ “exception” is a value that is thrown using the throw statement.
- `事由@
- `~promise$がなぜ`却下され$たかを指示する`値$。 ◎ “reason” is a value that indicates why a promise was rejected.
2. 要件
2.1 ~promiseの各種~状態
`~promise$は、[ `処理待ち@ / `履行済み@ / `却下済み@ ]のいずれかの状態にあることが要求される。 ~promiseは: ◎ A promise must be in one of three states: pending, fulfilled, or rejected.
`処理待ち$にあるときは: ◎ When pending, a promise:
- [ `履行済み$, `却下済み$ ]いずれかの状態に遷移し得る。 ◎ may transition to either the fulfilled or rejected state.
`履行済み$にあるときは: ◎ When fulfilled, a promise:
- 他の状態に遷移しては~MUST_NOT。 ◎ must not transition to any other state.
- `値$を持た~MUST。 この値は変化しては~MUST_NOT。 ◎ must have a value, which must not change.
`却下済み$にあるときは: ◎ When rejected, a promise:
- 他の状態に遷移しては~MUST_NOT。 ◎ must not transition to any other state.
- `事由$を持た~MUST。 この事由は変化しては~MUST_NOT。 ◎ must have a reason, which must not change.
ここで、
“変化しては~MUST_NOT”
とは、同一性について変異不可である(すなわち, ===
)を意味する。
深い変異不可性を含意するものではない
【 ~objectであれば その~memberの同一性が保たれる必要はない 】
。
◎
Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
【 定義~補足: 】
- `履行する@
- “`値$ %v で`~promise$ %promise を履行する” という句は、 %promise を`履行済み$状態に遷移させると同時に, %promise の`値$を %v にすることを意味する。
- `却下する@
- “`事由$ %r で`~promise$ %promise を却下する” という句は、 %promise を`却下済み$状態に遷移させると同時に, %promise の`事由$を %r にすることを意味する。
2.2 `then^es ~method
`~promise$は、[ その[ 現在の/最終的な ][ `値$, あるいは`事由$ ]に~accessするための, `then^es ~method ]を提供し~MUST。 ◎ A promise must provide a then method to access its current or eventual value or reason.
`~promise$ %promise の `then^es ~methodは、二個の引数を受容する: ◎ A promise’s then method accepts two arguments:
%promise.then(%onFulfilled, %onRejected)
引数 %onFulfilled, %onRejected は,いずれも省略可能である: ◎ Both onFulfilled and onRejected are optional arguments:
- ~functionでない %onFulfilled は無視され~MUST。 ◎ If onFulfilled is not a function, it must be ignored.
- ~functionでない %onRejected は無視され~MUST。 ◎ If onRejected is not a function, it must be ignored.
%onFulfilled は、それが~functionであるならば: ◎ If onFulfilled is a function:
- %promise が`履行され$た後に,その最初の引数に %promise の`値$を渡して ~callされ~MUST。 ◎ it must be called after promise is fulfilled, with promise’s value as its first argument.
- %promise が`履行され$る前に~callされては~MUST_NOT。 ◎ it must not be called before promise is fulfilled.
- 複数回~callされては~MUST_NOT。 ◎ it must not be called more than once.
-
%onRejected は、それが~functionであるならば: ◎ If onRejected is a function,
- %promise が`却下され$た後に、その最初の引数に %promise の`事由$を渡して ~callされ~MUST。 ◎ it must be called after promise is rejected, with promise’s reason as its first argument.
- %promise が`却下され$る前に~callされては~MUST_NOT。 ◎ it must not be called before promise is rejected.
- 複数回~callされては~MUST_NOT。 ◎ it must not be called more than once.
- %onFulfilled / %onRejected は、[ 実行文脈 ~stackが包含する~codeが,~platform~codeのみになる ]まで、~callされては~MUST_NOT。 [注 3.1] ◎ onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
- %onFulfilled / %onRejected は、 ~functionとして(すなわち, `this^es 値を伴わずに)~callされ~MUST。 [注 3.2] ◎ onFulfilled and onRejected must be called as functions (i.e. with no this value). [3.2]
-
同じ`~promise$上で, `then^es が複数回~callされることもあり得る — その際には: ◎ then may be called multiple times on the same promise.
- %promise が[ `履行され$たとき/`履行済み$にあるとき ]は ⇒ 渡された一連の %onFulfilled ~callbackは、それぞれの出自の `then^es が~callされた順序で実行され~MUST。 ◎ If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
- %promise が[ `却下され$たとき/`却下済み$にあるとき ]は ⇒ 渡された一連の %onRejected ~callbackは、それぞれの出自の `then^es が~callされた順序で実行され~MUST。 ◎ If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
-
`then^es は`~promise$を返さ~MUST。 [注 3.3] ◎ then must return a promise [3.3].
%promise2 = %promise1.then(%onFulfilled, %onRejected);
-
%onFulfilled / %onRejected が`値$ %x を返したならば
⇒
~promiseの解決~手順:
[[Resolve]](%promise2, %x)
を走らす。 ◎ If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x). - %onFulfilled / %onRejected が `例外$ %e を投出したならば ⇒ %promise2 は,`事由$ %e で`却下され$~MUST。 ◎ If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
- %onFulfilled は~functionでない, かつ %promise1 が`履行され$たならば ⇒ %promise2 は, %promise1 の値と同じ値で`履行され$~MUST。 ◎ If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
- %onRejected は~functionでない, かつ %promise1 が`却下され$たならば ⇒ %promise2 は, %promise1 の`事由$と同じ値で`却下され$~MUST。 ◎ If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
-
%onFulfilled / %onRejected が`値$ %x を返したならば
⇒
~promiseの解決~手順:
2.3 ~promiseの解決~手順
~promiseの解決~手順
は、`~promise$, および`値$を入力にとる抽象~演算であり、
[[Resolve]](%promise, %x)
と表記される。
%x が`~thenable$であるならば、それは
%promise に %x の状態を取入れさせようと試みる
— %x が,少なくとも~promiseに似た何かであるかのように挙動する前提の下で。
~~他の場合、それは,値 %x で %promise を`履行する$。
◎
The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x). If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.
この~thenableの扱いにより、~promise実装は,[ ~thenableが Promises/A+ に準拠する `then^es ~methodを公開する ]限りにおいて,相互運用可能になる。 それはまた, Promises/A+ 実装が、不適合な実装を,適度な `then^es ~methodで “消化-” できるようにする。 ◎ This treatment of thenables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliant then method. It also allows Promises/A+ implementations to “assimilate” nonconformant implementations with reasonable then methods.
[[Resolve]](%promise, %x)
を走らすときは、次の手続きを実行0する:
◎
To run [[Resolve]](promise, x), perform the following steps:
【 以下に現れる記号[ ~LET (初期化), ~SET (代入), ~IF, ~ELSE, ~RET(実行制御), 等々 ]の意味や定義の詳細は,~SYMBOL_DEF_REFを~~参照。 】
- %promise と %x が同じ~objectを指すならば ⇒ `事由$ `TypeError^es で %promise を`却下する$ ◎ If promise and x refer to the same object, reject promise with a TypeError as the reason.
-
~IF %x は`~promise$である ⇒ その状態を取入れる [注 3.4] — すなわち, %x の状態に応じて: ◎ If x is a promise, adopt its state [3.4]:
- `処理待ち$
- %promise は、[ %x が`履行され$る/`却下され$る ]まで,`処理待ち$であり続け~MUST — %x の状態が遷移したときは、遷移~~後の状態に応じて,下の対応する段を~~実行することになる。 ◎ If x is pending, promise must remain pending until x is fulfilled or rejected.
- `履行済み$
- %x の`値$で %promise を`履行する$ ◎ If/when x is fulfilled, fulfill promise with the same value.
- `却下済み$
- %x の`事由$で %promise を`却下する$ ◎ If/when x is rejected, reject promise with the same reason.
-
~ELIF %x が~object/~functionである: ◎ Otherwise, if x is an object or function,
-
%then ~LET
%x.`then^es
[注 3.5] ◎ Let then be x.then. [3.5] - ~IF 前段で `then^es を取得するときに、`例外$ %e が投出された ⇒ `事由$ %e で %promise を`却下する$ ◎ If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
-
~IF %then は~functionである: ◎ ↓
-
[ `this^es 値 ~SET %x ]の下で,次の二つの引数を渡して %then を~callする: ◎ If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:
-
最初の引数 %resolvePromise は、`値$ %y を伴って~callされたときに,
[[Resolve]](%promise, %y)
を走らす~functionである。 ◎ If/when resolvePromise is called with a value y, run [[Resolve]](promise, y). - 二番目の引数 %rejectPromise は、`事由$ %r を伴って~callされたときに, %r で %promise を`却下する$~functionである。 ◎ If/when rejectPromise is called with a reason r, reject promise with r.
- ただし、[ %resolvePromise, %rejectPromise の両者, または いずれか ]が(合計で)複数回 ~callされたときは、最初の~callが~~優先され、以降の~callは無視される【何もしない】。 ◎ If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
-
最初の引数 %resolvePromise は、`値$ %y を伴って~callされたときに,
-
~IF %then の~callにより,`例外$ %e が投出された: ◎ If calling then throws an exception e,
- ~IF %resolvePromise, %rejectPromise いずれかが~callされていた ⇒ %e を無視する ◎ If resolvePromise or rejectPromise have been called, ignore it.
- ~ELSE ⇒ `事由$ %e で %promise を`却下する$ ◎ Otherwise, reject promise with e as the reason.
-
- ~ELSE ⇒ %x で %promise を`履行する$ ◎ If then is not a function, fulfill promise with x.
-
%then ~LET
- ~ELSE ⇒ %x で %promise を`履行する$ ◎ If x is not an object or function, fulfill promise with x.
`~promise$が,[[[
[[Resolve]](%promise, thenable)
の再帰的な性質により,最終的に
[[Resolve]](%promise, thenable)
が再び~callされることになる
]ような,循環的な`~thenable$の連鎖
]に寄与するような`~thenable$
]で解決されたならば、上の~algoをなぞることは,無限~再帰をもたらすことになる。
実装には、要求されてはいないが,~such再帰を検知して,
`事由$ `TypeError^es で %promise を`却下する$ことが奨励される。
[注 3.6]
◎
If a promise is resolved with a thenable that participates in a circular thenable chain, such that the recursive nature of [[Resolve]](promise, thenable) eventually causes [[Resolve]](promise, thenable) to be called again, following the above algorithm will lead to infinite recursion. Implementations are encouraged, but not required, to detect such recursion and reject promise with an informative TypeError as the reason. [3.6]
3. 注記
ここでの “~platform~code” は、[ ~engine/環境/~promise ]の実装~codeを意味する。 実施においては、この要件により,[ %onFulfilled / %onRejected が,[ `then^es が~callされた~event-loopの~turn ]の後に,新鮮な~stackの下で,非同期的に実行される ]ことが確保される。 これは、 `setTimeout^c や `setImmediate^c などによる “~macro-task” , あるいは `MutationObserver^c や `process.nextTick^c などによる “~micro-task” の仕組みにより,実装し得る。 ~promise実装は,~platform~codeと見なされるので、それ自身も[ 中で~handlerが~callされるような,[ ~taskを~scheduleする待行列や “~trampoline” 【trampoline】 ]]を包含することもある。 ◎ Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
すなわち `this^es は、~strict~modeの下では,それらの内側では `undefined^es になり、 ~sloppy~modeの下では,~global~objectになる。 ◎ That is, in strict mode this will be undefined inside of them; in sloppy mode, it will be the global object.
実装は、
%promise2 === %promise1
を許容して~MAY — 実装がすべての要件を満たす限りにおいて。 各~実装は、どの条件の下で%promise2 === %promise1
になり得るかについて文書化するべきである。 ◎ Implementations may allow promise2 === promise1, provided the implementation meets all requirements. Each implementation should document whether it can produce promise2 === promise1 and under what conditions.一般に、 %x が真の`~promise$であることは、現在の実装から来ているときにのみ,知り得る【?】。 この条項により、実装は,自身に固有の手段を利用して[ 適合することが既知である~promise ]の状態を,取入れることが~~可能になる。 ◎ Generally, it will only be known that x is a true promise if it comes from the current implementation. This clause allows the use of implementation-specific means to adopt the state of known-conformant promises.
この,[ まず
%x.`then^es
への参照を格納した上で,その参照を試験してから,その参照を~callする ]手順により、[ 複数回にわたる%x.`then^es
~propertyへの~access ]は避けられる。 ~such用心は、[ 各~取得の合間に値が変化し得るような,~accessor~propertyの一面 ]における一貫性を確保するために,重要になる。 ◎ This procedure of first storing a reference to x.then, then testing that reference, and then calling that reference, avoids multiple accesses to the x.then property. Such precautions are important for ensuring consistency in the face of an accessor property, whose value could change between retrievals.実装は、`~thenable$が成す連鎖の深さに恣意的な上限を設定するべきでない — 再帰は,その恣意的な上限を超えて無限になるものと見做すべきである。 `TypeError^es をもたらすのは、真の循環に限られるべきである — 別個の~thenableからなる無限~連鎖に遭遇したときの無限再帰は、正しい挙動である。 ◎ Implementations should not set arbitrary limits on the depth of thenable chains, and assume that beyond that arbitrary limit the recursion will be infinite. Only true cycles should lead to a TypeError; if an infinite chain of distinct thenables is encountered, recursing forever is the correct behavior.