Promise を利用する仕様を書くとき — Writing Promise-Using Specifications

様々なアイデア集 — 2017 年 1 月 3 日

© CC0 To the extent possible under law, the editors have waived all copyright and related or neighboring rights to this work. In addition, as of 3 January 2017, the editors have made this specification available under the Open Web Foundation Agreement Version 1.0, which is available at http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0. Parts of this work may be from another specification document. If so, those parts are instead covered by the license of that specification document.

要約

この文書は、promise を[ 作成する/ 受容する/ 操作する ]ような仕様を書くための手引きを与える。 また、 promise を利用する仕様を記すときに 注釈文にて利用できる,一連の略記法についても述べる。 This document gives guidance on how to write specifications that create, accept, or manipulate promises. It also includes a series of prose shorthands you can use in your specification to work with promises.

1. 序論

~promise ( “約束” )とは、[ 単独の非同期的な演算 ]の最終的な結果を表現する,~objである。 それは,非同期的な関数からも返し得るので、使用者は,演算が成功した/失敗したときに~callされるような~callbackを~queueすることのみならず、返された~promise~objを操作して,様々な~~分岐の可能性を開けるようになる。 ◎ A promise is an object that represents the eventual result of a single asynchronous operation. They can be returned from asynchronous functions, thus allowing consumers to not only queue up callbacks to be called when the operation succeeds or fails, but also to manipulate the returned promise object, opening up a variety of possibilities.

~promiseは、 Dojo, jQuery, YUI, Ember, Angular, WinJS, Q, その他諸々の,~popularな~frameworkの一部分も含め,多くの JavaScript ~library間で互いに競いながら~testされてきた。 これは、遂には Promises/A+ ~community仕様和訳】 として完成され、ほとんどの~libraryはそれに適合している。 今や、標準の `Promise^c ~classが ECMAScript 言語~仕様に含まれている `ECMASCRIPT$r — 各種~web~platform~APIは、自身による非同期的な演算~用の~promiseを返せるようになっている。 ◎ Promises have been battle-tested in many JavaScript libraries, including as part of popular frameworks like Dojo, jQuery, YUI, Ember, Angular, WinJS, Q, and others. This culminated in the Promises/A+ community specification which most libraries conformed to. Now, a standard Promise class is included in the ECMAScript Language Specification [ECMASCRIPT], allowing web platform APIs to return promises for their asynchronous operations.

~promiseは今や、すべての “~one-and-done” 非同期的な演算~用の,~web~platformにおける~paradigmである。 以前まで、仕様は,そのような演算~用に,互いに合致しない 種々の仕組みを利用していた。 これからは、この型のすべての非同期的な演算は,代わりに~promiseを返すように指定されるべきである — ~platformに,非同期性のための統一化された~primitiveを与えるために。 ◎ Promises are now the web platform’s paradigm for all "one and done" asynchronous operations. Previously, specifications used a variety of mismatched mechanisms for such operations. Going forward, all asynchronous operations of this type should be specified to instead return promises, giving our platform a unified primitive for asynchronicity.

この文書は主に,仕様の書手たちに向けたのものであるが、~promiseを利用する ~library/~app を書く JavaScript 開発者にとっても有用になる。 特に仕様の書手~向けの節は、開発者が読み飛ばし易くするため,その旨が注記される。 ◎ Although the primary audience of this document is specification writers, it is also useful for JavaScript developers who want to write libraries or applications using promises. We will call out sections that are spec-writer specific so developers can skip them more easily.

2. ~promiseをいつ利用するか

2.1. ~one-and-done演算

~promiseは、主に,[[[ 単独の非同期的な演算 ]を~~開始させる~method ]から,返されるもの ]として利用される。 ~promiseを返す関数は、通常の同期的な関数とは対照的に,非同期的な関数†として考えるべきである — ここには とても強い相似があることを念頭に置いておけば、そのような関数を書いたり, それを理由付けるのも 容易くなる。 ◎ The primary use case for promises is returning them from a method that kicks off a single asynchronous operation. One should think of promise-returning functions as asynchronous functions, in contrast to normal synchronous functions; there is a very strong analogy here, and keeping it in mind makes such functions easier to write and reason about.

例えば: 通常の同期的な関数は、値を返すか, または 例外を投出する。 相似的に、非同期的な関数†は,[ ある値により充足されるか, または ある事由により却下される ]ような,~promiseを返すことになる。 同期的な関数が “~nothing”(すなわち, `undefined^jv )を返し得るのとちょうど同じく、非同期的な関数から返される~promiseも,~nothing( `undefined^jv )で充足し得る — この事例においては、~promiseの充足は,単純に非同期的な演算の完了を通達する。 【† より詳しく述べるなら、非同期的な演算を “包装する” 関数 — “その実行を約束する ~promise~objとして即時に(同期的に)返すような関数” の略記。】 ◎ For example, normal synchronous functions can either return a value or throw an exception. Asynchronous functions will, analogously, return a promise, which can either be fulfilled with a value, or rejected with a reason. Just like a synchronous function that returns "nothing" (i.e. undefined), promises returned by asynchronous functions can be fulfilled with nothing (undefined); in this case the promise fulfillment simply signals completion of the asynchronous operation.

そのような非同期的な演算の例は、各種~web仕様にありふれている — ~promiseの仕組みにより: ◎ Examples of such asynchronous operations abound throughout web specifications:

  • 非同期的な I/O 演算: ~storage~APIにより~dataを 読取る/書出す ~methodは、~promiseを返せるようになる。 ◎ Asynchronous I/O operations: methods to read or write from a storage API could return a promise.
  • 非同期的な~network演算: ~network越しに~dataを送受信する~methodは、~promiseを返せるようになる。 ◎ Asynchronous network operations: methods to send or receive data over the network could return a promise.
  • ~~時間のかかる計算: 何かを算出するために ある程度時間を~~要する~methodは、結果のための~promiseを返した上で,別の~thread上で働けるようになる。 ◎ Long-running computations: methods that take a while to compute something could do the work on another thread, returning a promise for the result.
  • 利用者に~~入力を促すとき: 利用者に回答を請う~methodは、~promiseを返せるようになる。 ◎ User interface prompts: methods that ask the user for an answer could return a promise.

以前までは、~web仕様は,非同期的な演算~用に,多種多様な~patternを利用していた。 これらは, 後述の付録 にて文書~化されているので、何が今や旧式と見なされているかについての~ideaを得られる。 今や、~platform~primitiveとして,~promiseがある — そのような~approachは最早~必要とされない。 ◎ Previously, web specifications used a large variety of differing patterns for asynchronous operations. We’ve documented these in an appendix below, so you can get an idea of what is now considered legacy. Now that we have promises as a platform primitive, such approaches are no longer necessary.

2.2. 一度限りの “~event”

~promiseは、すでに 充足された/却下された 後でも, 【まだ処理が終わっていないかのように,その結果が通達されるよう】 申込めるので、ある種の “~event” においては,とても有用になり得る。 一度だけ起こるものであって,作者が すでに生じた後にも その~statusを観測したいと欲することが多いとき、[ 充足されたのは,それが生じた時点とされる ]ような~promiseを供することは、とても簡便な~APIを与える。 ◎ Because promises can be subscribed to even after they’ve already been fulfilled or rejected, they can be very useful for a certain class of "event." When something only happens once, and authors often want to observe the status of it after it’s already occurred, providing a promise that becomes fulfilled when that eventuality comes to pass gives a very convenient API.

そのような “~event” の原型的な例として、~loadedかどうかを指示するものが挙げられる: 画像, ~font, あるいは文書などの資源でさえ、[[ 資源が全部的に~loadされたときに限り,充足される(あるいは,資源の~load時に~errorが~~生じたときは却下される) ]ような~promise ]を返すような, `loaded^c ~propertyを供し得る。 作者は常に、 `resource.loaded.then(onLoaded, onFailure)^c と記すことで,[ 資源の用意ができ次第,実行されることになる ]ような動作が~queueされる。 これは、[ 資源がすでに~loadされていたときでも, %onLoaded を実行する小taskを~queueする ]ように,働く。 対照的に,~event~modelでは、[ ~eventが発火された時点で,作者がまだ申込んでいない場合 ]には、その情報は失われる。 ◎ The prototypical example of such an "event" is a loaded indicator: a resource such as an image, font, or even document, could provide a loaded property that is a promise that becomes fulfilled only when the resource has fully loaded (or becomes rejected if there’s an error loading the resource). Then, authors can always queue up actions to be executed once the resource is ready by doing resource.loaded.then(onLoaded, onFailure). This will work even if the resource was loaded already, queueing a microtask to execute onLoaded. This is in contrast to an event model, where if the author is not subscribed at the time the event fires, that information is lost.

2.3. より一般的な,状態~遷移

ある種の事例においては、~promiseは、[ 状態~遷移を通達するための一般的な仕組み ]として,有用になり得る。 この用法には微妙な所があるが、正しく行われたなら、使用者にとって,とても使い勝手の良い~APIを供せる。 ◎ In certain cases, promises can be useful as a general mechanism for signaling state transitions. This usage is subtle, but can provide a very nice API for consumers when done correctly.

この~patternは、一度限りの “~event” 利用~事例の一般化と考えられる。 例えば、いくつかの `<img>^c 要素があるとする。 それらは、 `src^c ~propertyを再設定することにより,再~loadさせられる — すなわち,それらの状態は、 `~loaded^i から `未load^i へ戻るようにも遷移し得る。 したがって、 `~loaded^i になることは,一度限りではない: 代わりに、画像は,実際には[ `~loaded^i, `未load^i の状態を往復する ]ような状態~machineを成す。 そのような局面においては、それぞれの画像に[ ~promiseを返す `loaded^c ~property ]を与えることが,依然として有用になる — それは、次回の[ `~loaded^i 状態へ移行する状態~遷移 ]を通達する(あるいは、画像がすでに `~loaded^i 状態にあるならば,充足される~promiseを返す)。 この~propertyは、画像が `~loaded^i 状態から `未load^i 状態へ戻るまでは、取得0される度に,同じ~promiseを返すべきである。 `未load^i 状態へ戻ったなら、次回の `~loaded^i への遷移を表現するような,新たな~promiseが作成される。 ◎ One can think of this pattern as a generalization of the one-time "events" use case. For example, take <img> elements. By resetting their src property, they can be re-loaded; that is, they can transition back from a loaded state to an unloaded state. Thus becoming loaded is not a one-time occasion: instead, the image actually consists of a state machine that moves back and forth between loaded and unloaded states. In such a scenario, it is still useful to give images a promise-returning loaded property, which will signal the next state transition to a loaded state (or be already fulfilled if the image is already in a loaded state). This property should return the same promise every time it is retrieved, until the image moves backward from the loaded state into the unloaded state. Once that occurs, a new promise is created, representing the next transition to loaded.

`~loaded^i へ遷移し得る資源のみならず、~platformにおいて これが有用になり得る,多くの場面がある — 例えば: `終止^i へ遷移し得る~animation / `処分済み^i へ遷移し得る高価な資源 / `未load^i になり得る~cache など。 ◎ There are many places in the platform where this can be useful, not only for resources which can transition to loaded, but e.g. for animations that can transition to finished, or expensive resources that can transition to disposed, or caches that can become unloaded.

この~patternのちょっとした変種は、ある~classが[ 状態を遷移させるような~method ]を包含していて,その策定者が[ その状態~遷移が完了したときに,それを指示させたい ]と欲しているときに生じる。 その事例においては、[ その~methodが~promiseを返すようにする ]ことができる — その~classの~obj上の~propertyに指示させる代わりに。 `Streams API$ は、その `wait()^c / `close()^c ~methodに対し,この変種を利用する。 一般的に、~methodは動作~用に, ~propertyは情報的な状態~遷移~用に,利用されるべきである。 ◎ A slight variant of this pattern occurs when your class contains a method that causes a state transition, and you want to indicate when that state transition completes. In that case you can return a promise from the method, instead of keeping it as a property on your object. The streams API uses this variant for its wait() and close() methods. In general, methods should be used for actions, and properties for informational state transitions.

終わりに、この~patternの使い過ぎに注意。 あらゆる状態~遷移に,対応する~promise~propertyが必要になるわけではない。 有用になるかどうかの指標には、次が挙げられる: ◎ To close, we must caution against over-using this pattern. Not every state transition needs a corresponding promise-property. Indicators that it might be useful include:

  • 作者は、ほぼ常に,その状態~遷移の次の~instanceに関心があり、遷移が生じる度に繰返し通知が必要になるのは,稀なとき。 例えば、作者は,画像~要素が再loadされる度に それを知りたいと望むことは稀にしかない — 通例的には、単純に,画像の初回の~load, 場合によっては その `src^c を再設定した後に生じる次回の~loadに限られる。 ◎ Authors are almost always interested in the next instance of that state transition, and rarely need recurring notification every time it occurs. For example, rarely do authors care to know every time an image element is reloaded; usually they simply care about the initial load of the image, or possibly the next one that occurs after resetting its src.
  • 作者は、すでに生じた遷移に反応することに関心があることが多いとき。 例えば作者は、[ 画像が~loadされたとき, あるいは 画像がすでに~loadされていた場合は可能な限り早くに,何らかの~codeを走らせたい ]と欲することが多いとき。 ◎ Authors are often interested in reacting to transitions that have already occurred. For example, authors often want to run some code once an image is loaded; if the image is already loaded, they want to run the code as soon as possible.

3. ~promiseを利用すべきでないとき

~promiseは、多種の非同期的な演算に対し,広範に適用-可能であるが、非同期性のためであっても,依然として適切でない状況はある。 ◎ Although promises are widely applicable to asynchronous operations of many sorts, there are still situations where they are not appropriate, even for asynchronicity.

3.1. 繰返される~event

複数回~生じ得るような どの~eventも、~promiseの “~one-and-done” ~modelに対する良い候補にはならない。 ~promiseに対し、代わりに一連の~eventを表現するような,単独の非同期的な演算はない。 ここでは、従来の `EventTarget^c の用法で~~十分である。 ◎ Any event that can occur more than once is not a good candidate for the "one and done" model of promises. There is no single asynchronous operation for the promise to represent, but instead a series of events. Conventional EventTarget usage is just fine here.

3.2. ~streaming~data

~dataが巨大にもなることもあって, 増分的に生産し得るものである場合、~promiseはおそらく,正解ではない。 代わりに、開発~下にある `Streams API$ を利用したいと欲するであろう — それは、作者が,~data~streamを[ その内容~全体を~memory内に~bufferすることなく処理して,増分的に組上げる ]ことを~~可能にする。 ◎ If the amount of data involved is potentially large, and could be produced incrementally, promises are probably not the right solution. Instead, you’ll want to use the under-development streams API, which allows authors to process and compose data streams incrementally, without buffering the entire contents of the stream into memory.

注記: すべての~dataが~memory内に~bufferされても懸念にならないような事例に対しては、便宜のため,Streams API の傍系として~promise~APIを供せる場合もあるが、あくまで補助的な役割になるであろう。 ◎ Note that in some cases, you could provide a promise API alongside a streaming API, as a convenience for those cases when buffering all the data into memory is not a concern. But this would be a supporting, not primary, role.

4. ~API設計の手引き

仕様の~APIにおいて~promiseを利用したり受容することには、少数の微妙な側面がある。 ここでは、よくある質問や状況に取組む。 ◎ There are a few subtle aspects of using or accepting promises in your API. Here we attempt to address commonly-encountered questions and situations.

4.1. ~error

4.1.1. ~promiseを返す関数は,常に~promiseを返すべきである

~promiseを返す関数は、すべての状況下で,常に~promiseを返すべきである — 結果が同期的に可用であっても, あるいは 入力が妥当でないことを同期的に検出し得るとしても、この情報は,統一的な~channelを通して通信される必要がある。 開発者が次のように書けば: ◎ Promise-returning functions should always return a promise, under all circumstances. Even if the result is available synchronously, or the inputs can be detected as invalid synchronously, this information needs to be communicated through a uniform channel so that a developer can be sure that by doing

promiseFunction()
  .then(%onSuccess)
  .catch(%onFailure);

すべての成功/~errorを取扱えるようにするため。 ◎ they are handling all successes and all errors.

特に,~promiseを返す関数は、決して,~errorを同期的に投出するべきでない — そうすると、その使用者に~error取扱い~logicの重複を強いることになるので: catch (%e) { ... } ~block内に一つ, .catch(%e 射 { ... }) ~block内にもう一つ。 引数を検証する際に~errorになるときでも、すべての~errorは、却下される~promiseを返すことにより,通達されるべきである。 ◎ In particular, promise-returning functions should never synchronously throw errors, since that would force duplicate error-handling logic on the consumer: once in a catch (e) { ... } block, and once in a .catch(e => { ... }) block. Even argument validation errors are not OK. Instead, all errors should be signaled by returning rejected promises.

【 記法 %args `射@ %code は、 %args を入力に %code を実行する無名~関数( arrow function )。 】

WebIDL ~based仕様については、この~~原則は WebIDL 仕様により 自動的に守られる: WebIDL の[ `演算$や, `多重定義~解決~algo$自身 ]により投出される,どの例外も、自動的に 却下に変換される。 手動により検証する方法の例は、後述の `validatedDelay^c 例 を見よ。 ◎ For WebIDL-based specs, this is taken care of automatically by the WebIDL specification: any exceptions thrown by WebIDL operations, or by the WebIDL overload resolution algorithm itself, are automatically converted into rejections. For an example of how to do manual validation, see the validatedDelay example below.

4.1.2. 却下~事由は `Error^c にするべきである

~promiseの却下~事由は、常に ECMAScript `Error^c 型の~instanceにされるべきである — 同期的に投出される例外が、常に `Error^c の~instanceにされるべきであることと,ちょうど同様に。 ◎ Promise rejection reasons should always be instances of the ECMAScript Error type, just like synchronously-thrown exceptions should always be instances of Error as well.

特に,~DOMや他の~web~platform仕様においては、このことは、 `DOMError^c を決して利用せず,代わりに[ WebIDL により 拡張された `Error^c である, `DOMException$c ]が利用されるべきであることを意味する。 もちろん、 組込みの ECMAScript ~error型 も利用できる。 ◎ In particular, for DOM or other web platform specs, this means you should never use DOMError, but instead use DOMException, which per WebIDL extends Error. You can of course also use one of the built-in ECMAScript error types.

4.1.3. 却下は、例外的な状況に対し利用されるべきである

正確に何を以って “例外的” と見なすかは,議論になるのが常だが、 ~API仕様において~promiseを`却下する$ことにする前に,常に自問するべきである: この関数が同期的であったとするとき、この状況下で,例外の投出-を期待するだろうか? あるいは,( `null^jv, `false^jv, `undefined^jv のような)失敗~値を期待するだろうか? ~APIの使用者にとっては,いずれの挙動がより有用になるか?について、考えるべきである。 不確かなときは、~APIが同期的であると仮定した下で,開発者たちが例外の投出を期待するかどうか、考えるべきである。 ◎ What exactly you consider "exceptional" is up for debate, as always. But, you should always ask, before rejecting a promise: if this function was synchronous, would I expect a thrown exception under this circumstance? Or perhaps a failure value (like null, false, or undefined)? You should think about which behavior is more useful for consumers of your API. If you’re not sure, pretend your API is synchronous and then think if your developers would expect a thrown exception.

却下が~~適切になる事例には、次がある: ◎ Good cases for rejections include:

  • ~storageへの書込みや~networkからの読取りなどの I/O 演算に失敗したとき。 ◎ A failed I/O operation, like writing to storage or reading from the network.
  • 要請された~taskを完了させることが不可能なとき: 例えば、演算が `accessUsersContacts()^c 【“利用者の連絡先情報に~accessする”】 であって,利用者が不許可にしたなら、何かで却下される~promiseを返すべきである。 ◎ When it will be impossible to complete the requested task: for example if the operation is accessUsersContacts() and the user denies permission, then it should return a rejected promise.
  • 非同期的な演算を試みている間に,内的に何かが壊れているような状況: 例えば、開発者から妥当でない~dataが渡された, あるいは 環境がこの演算に対し妥当でない状態にあるなど。 ◎ Any situation where something is internally broken while attempting an asynchronous operation: for example if the developer passes in invalid data, or the environment is in an invalid state for this operation.

却下が~~不適切になる事例には、次がある: ◎ Bad uses of rejections include:

  • 非同期的に請われた値を,見出せなかったとき: 例えば, 【“非同期的な map” asyncMap があるとして,】 `asyncMap.get(key)^c は、 %key に対する~entryがないときは, `undefined^jv に対する~promiseを返すべきである。 同様に、 `asyncMap.has(key)^c は, `false^jv に対する~promiseを返すべきである。 %key の不在は,例外的でないであろうから、却下される~promiseを返すのは,拙い選択になるであろう。 ◎ When a value is asked for asynchronously and is not found: for example asyncMap.get("key") should return a promise for undefined when there is no entry for "key", and similarly asyncMap.has("key") should return a promise for false. The absence of "key" would be unexceptional, and so a rejected promise would be a poor choice.
  • 演算は何かを問うものであり, その回答が否定的になるとき: 例えば演算が `hasPermissionToAccessUsersContacts()^c 【“利用者の連絡先情報への~accessは許可されているか?”】 であって,利用者が不許可にしたときは、`却下する$のでなく, `false^jv で充足される~promiseを返すべきである。 ◎ When the operation is phrased as a question, and the answer is negative: for example if the operation is hasPermissionToAccessUsersContacts() and the user has denied permission, then it should return a promise fulfilled with false; it should not reject.

審判が必要とされるような事例には、次がある: ◎ Cases where a judgement call will be necessary include:

  • 問いなのか請求なのかについて、より あいまいな~API: 例えば `requestUsersContacts()^c 【“連絡先情報の~~入力を要請する”】 は、利用者が不許可にしたとき, `null^jv で充足される~promiseを返すことも,あるいは[ 利用者が不許可にしたことを明言する~error ]で却下される~promiseを返すことも考え得る。 ◎ APIs that are more ambiguous about being a question versus a demand: for example requestUsersContacts() could return a promise fulfilled with null if the user denies permission, or it could return a promise rejected with an error stating that the user denied permission.

4.2. 非同期的な~algo

この節は、主に仕様の書手~向けである。 ここでは、注釈文において,非同期的な~algo~flowを明白化するときの~vagariesについて扱う。 これについての背景は、 非同期性についての Anne 氏の~blog-post †を見よ。 【† 要約:非同期に実行される~algoの中で,不用意に大域的な~propertyを汚すと、並列的に実行されている JavaScript ~codeの信頼性を確保できなくなる。】 ◎ This section is primarily for spec writers, dealing with the vagaries of clearly manifesting asynchronous algorithm flow in prose. For more background on this subject, see Anne’s blog post on asynchronicity.

4.2.1. ~promiseを単純に解決する/却下するとき

~callbackの旧世界と違って、 成功, ~error それぞれの事例に対し,別々の~callback型を(例えば WebIDL において)作成する必要はない。 代わりに,単に~promiseを `解決する$/`却下する$ だけでよい。 ◎ Unlike in the old world of callbacks, there’s no need to create separate callback types (e.g. in WebIDL) for your success and error cases. Instead, just resolve or reject your promise.

4.2.2. 並列的な手続きを明示的に注記する

仕様の~algo内のどの手続きが,作者の JavaScript ~codeと並列的に走るか — すなわち,~script実行を阻まないことになるか — について注記することは、重要である。 これは、~algoの実装者に[ どの演算が,例えば~background~threadや非同期的な I/O ~callを利用する必要があるか ]について指図する。 また、作者たちにとっては,[ ~algoの演算に関して 彼らの演算に期待されている配列順序 ]を知る一助になる。 そのためには、 `HTML$r の “`並列的$に” という句を利用する。 ◎ It is important to note which steps in your algorithms will be run in parallel with the author’s JavaScript code, i.e. without blocking script execution. This instructs implementers as to which operations will need to use e.g. a background thread or asychronous I/O calls. And it helps authors to know the expected sequencing of their operations with respect to those of your algorithm. To do this, use the phrase in parallel from [HTML].

例として、次の手続きは、 %ms ~ms後に`解決される~promise$を与える: ◎ As an example, the following steps will give a promise that is resolved after ms milliseconds:

  1. %p ~LET `新たな~promise$ ◎ Let p be a new promise.
  2. 次の手続きを`並列的$に走らせる: ◎ Run the following steps in parallel:

    1. %ms ~ms待機する ◎ Wait ms milliseconds.
    2. `undefined^jv で %p を`解決する$。 ◎ Resolve p with undefined.
  3. ~RET %p 【 上の下位手続きが終わるのを待たずに】 ◎ Return p.

“並列的に走らせる” の見出しを省略したなら、~algoは,実装者に[ ~main~threadを %ms ~ms間~阻む ]よう指図することになり、とてもまずい! — 上のように書かれたなら、~algo自身は阻まれることなく,待機することを正しく述べることになる。 ◎ If we had omitted the "Run the following steps in parallel" heading, then the algorithm would have instructed implementers to block the main thread for ms milliseconds, which is very bad! Whereas as written, this algorithm correctly describes a non-blocking wait.

4.2.3. 開発者~codeを呼出す~taskは~queueする

~promiseは、非同期~演算について開発者に通知することに関して,多くの詳細を抽象化する。 例えば、 %x を引数に,~callback %cb を~callする`~taskを~queueする$ と記す代わりに、 %x で %p を`解決する$ と記すことができる — そうすれば、[ それが通常の~promiseの仕組みを利用することになる ]と解されるようになる(すなわち,開発者は、~promiseの `then^c ~methodに~callbackを渡すことにより、その 充足/却下 を待機できる — それは,次回の小taskにおいて それらの~callbackを~callすることになる)。 なので、~promise~based非同期的な~algoの内側では、ほとんどの事例において,明示的に~taskを~queueするように記す必要はなくなる。 ◎ Promises abstract away many of the details regarding notifying the developer about async operations. For example, you can say "resolve p with x" instead of e.g. "queue a task to call the callback cb with x," and it’s understood that this will use the normal promise mechanisms. (Namely, the developer can wait for fulfillment or rejection by passing callbacks to the promise’s then method, which will call those callbacks in the next microtask.) So in most cases, you will not need to explicitly queue tasks inside your promise-based asynchronous algorithms.

しかしながら、[ ~promiseが仲介し得るものを超える仕方で,開発者~codeと応接する必要がある所 ]では、依然として,~taskを~queueするように記す必要がある。 例えば、[ 開発者による~event~handlerの中へ~callし得る ]ような~eventを,発火させたいと欲することもあろう。 あるいは、有構造clone演算を遂行する必要があるかもしれない — それは、 取得子を誘発し得る 【要約: Object.defineProperty で設定された get を誘発し得る — すなわち,これも開発者~codeを~callし得る】 。 これらが,~algoの非同期的な部位の内側で行われなければならない場合、[ 特定の~task~queueを介して, および それに入れられる~taskにより,行われる ]ことを指定する必要がある。 これは、[ そのような~APIの開発者から観測-可能な演算が起こる,正確な時機 ]を,[ その~queueされる他の~task ], および[ ~promiseにより利用される小task~queue ]の両者から相対的な時点に確定させる。 ◎ However, in cases where you need to interface with developer code in more ways than can be mediated via the promise, you’ll still need to queue a task. For example, you may want to fire an event, which can call into developer event handlers. Or you may need to perform a structured clone operation, which can trigger getters. If these things must be done inside the asynchronous portion of your algorithm, you need to specify that they are done via a queued task, and with a specific task queue. This nails down the exact time such developer-observable operations happen both in relation to other queued tasks, and to the microtask queue used by promises.

例として、次の手続きは, %ms ~ms後に`解決される~promise$を返すことになるが、 `window^c 上にも名前 `timerfinished^et の~eventを発火する: ◎ As an example, the following steps will return a promise resolved after ms milliseconds, but also fire an event named timerfinished on window:

  1. %p ~LET 新たな~promise ◎ Let p be a new promise.
  2. 次の手続きを`並列的$に走らせる: ◎ Run the following steps in parallel:

    1. %ms ~ms間~待機する ◎ Wait ms milliseconds.
    2. `undefined^jv で %p を`解決する$ ◎ Resolve p with undefined.
    3. 次を走らす`~taskを~queueする$ ⇒ `閲覧文脈$にて`作動中の文書$の `Window$c ~objに向けて,名前 `timerfinished^et の`~eventを発火する$ ◎ Queue a task to fire a simple event named timerfinished at the browsing context active document’s Window object.
  3. ~RET %p ◎ Return p.

4.3. ~promiseの受容-法

4.3.1. ~promise引数は解決されるべきである

一般的に、引数が~promiseであると期待される所では、~thenable 【 `then^c ~methodを備える~obj】 も非~promise値も許容されるべきである — 引数を利用する前に, `~promiseとして解決する$ ことにより。 決して,次をするべきでない:

  • 入力の値に対する 型~検出
  • ~promiseと他の値との間の`多重定義$
  • ~promiseを`共用体~型$ 内に置くこと
◎ In general, when an argument is expected to be a promise, you should also allow thenables and non-promise values by resolving the argument to a promise before using it. You should never do a type-detection on the incoming value, or overload between promises and other values, or put promises in a union type.

WebIDL を利用する仕様においては、これは, WebIDL `~promise型$により自動的に守られる。 JavaScript において それが意味する所を見るため、~promiseに %ms ~msの遅延を追加する,次の関数を考える: ◎ In WebIDL-using specs, this is automatically taken care of by the WebIDL promise type. To see what it means in JavaScript, consider the following function, which adds a delay of ms milliseconds to a promise:

function addDelay(%promise, %ms) {
    return Promise.resolve(%promise).then(%v `射$
        new Promise(%resolve 射
            setTimeout(() 射 %resolve(%v), %ms);
        )
    );
}

var %p1 = addDelay(doAsyncOperation(), 500);
var %p2 = addDelay("value", 1000);

この例においては、 %p1 は,[[ `doAsyncOperation()^c から返される %promise ]が充足されてから 500 ~ms後に,その演算の値で充足される ]ことになる(または、 %p1 は %promise が却下され次第,却下されることになる)。 また,入力の引数は`~promiseとして解決され$るので、この関数は,文字列 `value^l を渡したときにも働ける: %p2 は、1000 ~ms後に, `value^l で充足されることになる。 このような仕方で、本質的に,それを[ 値に対し即時に充足される~promise ]として扱っている。 ◎ In this example, p1 will be fulfilled 500 ms after the promise returned by doAsyncOperation() fulfills, with that operation’s value. (Or p1 will reject as soon as that promise rejects.) And, since we resolve the incoming argument to a promise, the function can also work when you pass it the string "value": p2 will be fulfilled with "value" after 1000 ms. In this way, we essentially treat it as an immediately-fulfilled promise for that value.

4.3.2. ~promiseを返す関数として開発者から給された関数は “~promise-callされる” べきである

仕様の策定者は、[[ ~promiseを返す関数を期待しているもの【引数など】 ]に対し,開発者から給された %関数 ]に対しては、[ %関数 が~thenableや非~promise値 を返す, あるいは 例外を投出する ]ことも許容するべきである。 また、これらすべての事例に対し,[ %関数 がそれに相似的な~promise 【すなわち, %関数 の返値で解決されるか, %関数 が投出した例外で却下される~promise】 を返した ]かのように扱うべきである。 この処理は、 “ %関数 を `~promise-call$する” と呼ばれる演算に~encapsulateできる。 これにより、同期的な形のときでも、成功/失敗に対し,非同期的な形のときと同じように反応させられる。 ◎ If the developer supplies you with a function that you expect to return a promise, you should also allow it to return a thenable or non-promise value, or even throw an exception, and treat all these cases as if they had returned an analogous promise. We can encapsulate this process in an operation called promise-calling the supplied function. This allows us to have the same reaction to synchronous forms of success and failure that we would to asynchronous forms.

これを利用する方法と,何故そうすべきかについての更なる論点は、後述の `resource.open^c 例 を見よ。 ◎ See the resource.open example below for further discussion of how and why this should be used.

5. 略記~用の句

この節は、主に仕様の書手~向けである。 ここでは、共通的な各種~promise演算の遂行-法を,注釈文にてどう述べるかについて扱う。 ◎ This section is primarily for spec writers, dealing with ways of performing common promise operations in prose.

この節は、最終的には, WebIDL の当該箇所に移行されるべきである。 #27 を見よ。 ◎ This section should eventually move to WebIDL, where it belongs. See #27.

仕様を書くときは、共通的な~promise演算を簡潔に参照できると簡便である。 以下では、それを行えるようにするための,一連の略記を定義する。 ◎ When writing such specifications, it’s convenient to be able to refer to common promise operations concisely. We define here a set of shorthands that allow you to do so.

【 以下に現れる語 “充足~値”/“事由~値” の “充足”/“事由” は、値の役割を明らかにするための呼称であり、値の範囲に何らかの要件を課すものではない(一部は、訳者による補完である)。 】

5.1. ~promiseの作成-法

`新たな~promise@ とは、[ 初期化されているが, まだ解決されていない,新たな~promise~obj ]を,更に操作するためのものとして与えるものである。 それは、次を~callすることに等価である ⇒ new Promise((%resolve, %reject) `射$ { … })

ここで:

◎ "A new promise" gives a new, initialized-but-unresolved promise object to manipulate further. It is equivalent to calling new Promise((resolve, reject) => { ... }), using the initial value of the Promise constructor. Here ... stands in for code that saves the value of resolve and reject for later use by the shorthands under §5.2 Manipulating Promises.

( 充足~値 %x ) が与えられている下における,句 %x で解決される(新たな)~promise とは、 `Promise.resolve(x)^c による結果の~promiseを~~表す略記である — ここで:

  • `Promise.resolve^c には、 `Promise.resolve$c の`初期値$を利用する。

そのような結果を得ることを、 %x を `~promiseとして解決する@ とも記す。

◎ "A promise resolved with x" or "x resolved as a promise" is shorthand for the result of Promise.resolve(x), using the initial value of Promise.resolve.

( 事由~値 %r ) が与えられている下における,句 %r で却下される(新たな)~promise とは、 `Promise.reject(r)^c の結果を~~表す略記である — ここで:

  • `Promise.reject^c には、 `Promise.reject$c の`初期値$を利用する。
◎ "A promise rejected with r" is shorthand for the result of Promise.reject(r), using the initial value of Promise.reject.

【 内部ではすでに過去であっても, “解決される” / “却下される” と記しているのは、外部に対しては %resolve / %reject を通して非同期に(未来に)通達されることになるためである。 冗長に書くならば、 “解決されるのは確定しているが,まだ そのときに行う処理は呼出されていない” 等々の様になるであろう。 】

5.2. ~promiseの操作-法

( ~promise %p, 充足~値 %x ) が与えられている下における,句 %x で %p を 解決する とは、[[ %p の作成-時に格納済みの, %resolve 関数 ]に,引数 %x を渡して~callする ]ことを表す略記である。 ◎ "Resolve p with x" is shorthand for calling a previously-stored resolve function from creating p, with argument x.

( ~promise %p, 事由~値 %r ) が与えられている下における,句 %r で %p を `却下する@ とは、[[ %p の作成-時に格納済みの, %reject 関数 ]に,引数 %r を渡して~callする ]ことを表す略記である。 ◎ "Reject p with r" is shorthand for calling a previously-stored reject function from creating p, with argument r.

【 同じ “解決-”, “却下-” が、新たな~promiseを作成するときと,操作するときの両者に用いられてることに注意。 “%x で`解決される~promise$” は、 “`新たな~promise$ %p を作成してから, %x で %p を`解決-$した結果の %p” と同じものになるように見受けられる(が、確かめたわけではない)。 】

これらの略記が,`並列的$に走らせている~algoの中で利用されている場合、 %p に`関連する設定群~obj$の`担当の~event~loop$上に,格納済みの関数を~callする`~taskを~queueする$。 ◎ If the algorithm using these shorthands is running in parallel, the shorthands queue a task on p’s relevant settings object’s responsible event loop to call the stored function.

5.3. ~promiseに対する反応-法

( ~promise %p, 充足~値 %v ) が与えられている下における,句 %v による %p の `充足-時@ には…( `下位手続き^V ) とは、[ %p.then(%onFulfilled) が 【 %onFulfilled が~callされる前に】 ~callされている ]ことを表す略記である† — ここで:

  • %p.then には、 `Promise.prototype.then$c の`初期値$を利用する。
  • 後続して与えられる `下位手続き^V が %onFulfilled 関数を成す。
  • 充足~値 %v が,[ `下位手続き^V が %onFulfilled の引数として~accessする値 ]を与える††。
◎ "Upon fulfillment of p with value v" is shorthand for calling p.then(onFulfilled), with the successive nested steps comprising the onFulfilled function, and using the initial value of Promise.prototype.then. The steps then have access to onFulfilled’s argument as v.

( ~promise %p, 事由~値 %r ) が与えられている下における,句 %r による %p の `却下-時@ には…( `下位手続き^V ) とは、[ %p.then(`undefined^jv, %onRejected) が 【 %onRejected が~callされる前に】 ~callされている ]ことを表す略記である† — ここで:

  • %p.then には、 `Promise.prototype.then$c の`初期値$を利用する。
  • 後続して与えられる `下位手続き^V が %onRejected 関数を成す。
  • 事由~値 %r が,[ `下位手続き^V が %onRejected の引数として~accessする値 ]を与える††。
◎ "Upon rejection of p with reason r" is shorthand for calling p.then(undefined, onRejected), with the successive nested steps comprising the onRejected function, and using the initial value of Promise.prototype.then. The steps then have access to onRejected’s argument as r.
  • これらの句の用例
  • † これらの句 〜時には… は、 “〜されたとき” に加えて, “すでに〜されていたとき” も含む — ~algoの中で,これらの句に遭遇した時点で、当の~promiseは すでに[ 充足-/却下- ]されている場合もあり得るので。
  • †† `下位手続き^V の中で,引数が利用されない場合、充足~値/事由~値が省略されて,単に %p の充足-時には… 等と記されることもある。
  • ~promiseが `決着-@ ( settle )した(あるいは, “する”, “される”, 等々)という語も用いられる(他の仕様にも現れる)。 これは、~promiseが充足されたか却下された(が,まだ[ 充足-時/却下-時 ]の処理は行われていない)ことを表す総称を意味する。

( ~promise %p, 充足~handler %fulfillmentHandler (省略可), 却下~handler %rejectionHandler (省略可) ) が与えられている下における,句 (それらの~handlerで) %p を `変形する@ とは、[ %p.then(%fulfillmentHandler, %rejectionHandler) を~callする ]ことを表す略記である — ここで:

  • %p.then には、 `Promise.prototype.then$c の`初期値$を利用する。
  • 【 充足~handler/却下~handlerが省略されている場合、 `undefined^jv として扱うものと見られる。 (両者とも省略されることはまず無いであろうが。) 】
◎ "Transforming p with a fulfillment and/or rejection handler" is shorthand for calling p.then(fulfillmentHandler, rejectionHandler), using the initial value of Promise.prototype.then.

後者の句の例を挙げる: ◎ Some examples of the latter phrase would be

  1. [ `undefined^jv を返す 充足~handler ]で %p を`変形した$結果を返す。 ◎ Return the result of transforming p with a fulfillment handler that returns undefined.

あるいは ◎ or

  1. [ 第一~引数の二倍を返す 充足~handler ]で %p を`変形した$結果を返す。 ◎ Return the result of transforming p with a fulfillment handler that returns twice its first argument.

これらは順に、次に対応する: ◎ These correspond to

return %p.then(() `射$ undefined);

, ◎ and

return %p.then(%x 射 2 * %x);

(もちろん,より複雑な変形-も可能である — 下の `resource.open^c 例 に示されるように。) ◎ respectively. (More complicated transforms are of course possible as well, as shown in the resource.open example below.)

5.4. 複数の~promiseの集成-法

いくつかの~promiseからなる~collectionが与えられた下で, その `すべてを待機する@ ときの結果は、 `Promise.all(promiseArray)^c を~callして作成される~promiseである — ここで:

  • `all^c には、 `Promise.all$c の`初期値$を利用する。
  • %promiseArray は、その~collectionが成す配列である。
◎ The result of waiting for all of a collection of promises is a promise created by calling Promise.all(promiseArray), where promiseArray is that collection in array form and we use the initial value of Promise.all.

この句は[ それぞれが~promiseを返すような,複数の非同期的な演算 ]を`並列的$に 遂行した上で,それら一まとめに対し 反応させたいと望むときに、有用になる。 結果の~promiseは:

  • 所与の~promiseすべてが充足されたならば ⇒ [ それらの充足~値からなる配列 ]で充足されることになる。
  • 他の場合、すなわち,いずれかの~promiseが却下されたならば ⇒ [ それらのうち最初に生じた却下~事由 ]で却下されることになる。
◎ This phrase is useful when you wish to perform multiple asynchronous operations in parallel that return promises, and then react to them all together. If all of the given promises fulfill, then the resulting promise will be fulfilled with an array corresponding to the fulfillment values. If any of them reject, then the resulting promise will be rejected with the first rejection reason to occur.

この句の用法~例は `batchRequest( urls )^c 節 に見出せる。 ◎ An example usage of this phrase is found in §6.7 batchRequest ( urls ).

5.5. ~promise-call法

関数 %f が与えられた下で, %f(...%args)~promise-call したときの結果は、次で与えられる: ◎ The result of promise-calling f(...args) is:

  • %f を~callして充足~値 %v が返されたならば ⇒ %v で`解決される~promise$ ◎ If the call returns a value v, the result of resolving v as a promise.
  • %f を~callして例外 %e が投出されたならば ⇒ %e で`却下される~promise$ ◎ If the call throws an exception e, a promise rejected with e.

JavaScript では、~promise-callは,次のように~~記せるであろう: ◎ In JavaScript, you might express promise-calling this way:

function promiseCall(%f, `...args^V) {
    try {
        return Promise.resolve(%f(`...args^V));
    } catch (%e) {
        return Promise.reject(%e);
    }
}

5.6. ~realmについての注記

すべての事例において、 Promise.resolve の `初期値@ の様な句を利用するときは、[[[ 指定されている関数(この場合は `resolve^c ) ]に結付けられている`~realm$† ]の中での初期値 ]を意味する。 例えば、 `window.f()^c が “`1^jv で`解決される~promise$” を返すものと指定されている場合: ◎ In all cases, when we use phrases like "the initial value of Promise.resolve," we mean the initial value within the realm associated to the function being specified. So for example, if window.f() is specified to return "a promise resolved with 1," then:

【† “~~領域” — 大雑把に言えば、[ 当の~codeが参照している, または作成した,~obj/関数 ]が “属する” ECMAScript 大域環境 】

assert(windowA.f().constructor === windowA.Promise);
assert(windowB.f().constructor === windowB.Promise);

/* 
関数が呼出されている~objの~realmは、効果を持たない
— 関数の~realmだけが,関係0する:
◎
The realm of the object the function is being invoked on has no effect;
the function’s realm is all that matters.
*/
assert(windowA.f.call(windowB).constructor === windowA.Promise);

/* 
次のものは、~promise構築子に対する変異も伝播することを意味する:
◎
This means mutations to the Promise constructor also propagate.
*/

windowA.Promise.prototype.foo = "bar";
assert(windowA.f().foo === "bar");
assert(windowB.f().foo === undefined);

/* 
`Promise.resolve^c の~algoは、~globalを診る代わりに,内在的な, 改変-不能な `~Promise0$ を利用するので、
名前 `Promise^l の~global~propertyを改変しても,返値には影響0しない
◎
But since the algorithm for Promise.resolve uses the un-modifiable %Promise% intrinsic, instead of consulting the global, modifying the global property named
"Promise" does not impact the return value.
*/
const %oldPromise = windowA.Promise;
windowA.Promise = () `射$ throw new Error(
    "開発者~codeは壊しますが,~platform~codeは壊しません。"
);
assert(windowA.f().constructor !== windowA.Promise);
assert(windowA.f().constructor === %oldPromise);

更なる情報については、 この www-tag ~thread を見よ — とりわけ返信を。 ◎ For more information, see this www-tag thread, especially the replies.

6. 例

この節は、ほぼ仕様の書手~向けであるが、仕様の注釈文を JavaScript に翻訳した例も与える。 ◎ This section is mostly for spec writers, although it does give examples of the spec prose translated into JavaScript.

6.1. `delay( ms )^c

`delay^c は、 %ms ~ms内に充足されることになる,~promiseを返す関数である。 ~promiseの解決は,一~行の注釈文で記せる: ◎ delay is a function that returns a promise that will be fulfilled in ms milliseconds. It illustrates how simply you can resolve a promise, with one line of prose.

  1. %ms ~LET ToNumber(%ms) ◎ Let ms be ToNumber(ms).
  2. %ms ~LET [ %ms ~EQ `NaN^jv ならば `+0^jv / ~ELSE_ { %ms, `+0^jv } の最大 ] ◎ If ms is NaN, let ms be +0; otherwise let ms be the maximum of ms and +0.
  3. %p ~LET 新たな~promise ◎ Let p be a new promise.
  4. 次の手続きを`並列的$に走らせる: ◎ Run the following steps in parallel:

    1. %ms ~ms間 待機する ◎ Wait ms milliseconds.
    2. `undefined^jv で %p を`解決する$ ◎ Resolve p with undefined.
  5. ~RET %p ◎ Return p.

JavaScript による等価な関数は,次の様になるであろう: ◎ The equivalent function in JavaScript would be

function delay(%ms) {
    %ms = Number(%ms);
    %ms = Number.isNaN(%ms) ? +0 : Math.max(%ms, +0);
    return new Promise(%resolve `射$ setTimeout(%resolve, %ms));
}

あるいは、指定された手続きに,より一対一に対応するように記すなら: ◎ or, in a more one-to-one correspondence with the specified steps,

function delay(%ms) {
    // 段 1, 2
    %ms = Number(%ms);
    %ms = Number.isNaN(%ms) ? +0 : Math.max(%ms, +0);

    // 段 3
    let %resolve;
    const %p = new Promise(%r 射 { %resolve = %r; });

    // 段 4
    setTimeout(() 射 %resolve(undefined), %ms);

    // 段 5
    return %p;
}

6.2. `validateddelay( ms )^c

`validatedDelay^c 関数は、その引数を検証することになる点を除いて, `delay^c 関数 とほぼ同様である。 これは、どの非同期的な演算であれ、その開始~前に即時の失敗を通達させるために,却下される~promiseを利用する方法を示す: ◎ The validatedDelay function is much like the delay function, except it will validate its arguments. This shows how to use rejected promises to signal immediate failure before even starting any asynchronous operations.

  1. %ms ~LET ToNumber(%ms) ◎ Let ms be ToNumber(ms).
  2. ~IF[ %ms ~EQ `NaN^jv ] ⇒ ~RET `TypeError^jv で`却下される~promise$ ◎ If ms is NaN, return a promise rejected with a TypeError.
  3. ~IF[ %ms ~LT ~zero ] ⇒ ~RET `RangeError^jv で`却下される~promise$ ◎ If ms is less than zero, return a promise rejected with a RangeError.
  4. %p ~LET 新たな~promise ◎ Let p be a new promise.
  5. 次の手続きを`並列的$に走らせる: ◎ Run the following steps in parallel:

    1. %ms ~ms間 待機する ◎ Wait ms milliseconds.
    2. `undefined^jv で %p を`解決する$ ◎ Resolve p with undefined.
  6. ~RET %p ◎ Return p.

JavaScript による等価な関数は,次の様になるであろう: ◎ The equivalent function in JavaScript would be

function delay(%ms) {
    %ms = Number(%ms);

    if (Number.isNaN(%ms)) {
        return Promise.reject(new TypeError("Not a number."));
    }
    if (%ms < 0) {
        return Promise.reject(new RangeError("ms must be at least zero."));
    }

    return new Promise(%resolve `射$ setTimeout(%resolve, %ms));
}

6.3. `addDelay( promise, ms )^c

`addDelay^c は、引数 %promise が`決着-$してから,返される~promiseが`決着-$するまでの合間に 余分な %ms ~msの遅延を追加する関数である。 引数 %promise を`~promiseとして解決する$ことで、非~promise値や~thenableも %promise に渡せるようにしている所に注目。 ◎ addDelay is a function that adds an extra ms milliseconds of delay between promise settling and the returned promise settling. Notice how it resolves the incoming argument to a promise, so that you could pass it a non-promise value or a thenable.

  1. %ms ~LET ToNumber(%ms) ◎ Let ms be ToNumber(ms).
  2. %ms ~LET[ %ms ~EQ `NaN^jv ならば `+0^jv / ~ELSE_ { %ms, `+0^jv } の最大 ] ◎ If ms is NaN, let ms be +0; otherwise let ms be the maximum of ms and +0.
  3. %p ~LET `新たな~promise$ ◎ Let p be a new promise.
  4. %resolvedToPromise ~LET %promise で`解決される~promise$ ◎ Let resolvedToPromise be the result of resolving promise to a promise.
  5. 充足~値 %v による %resolvedToPromise の`充足-時$には、次の手続きを`並列的$に遂行する: ◎ Upon fulfillment of resolvedToPromise with value v, perform the following steps in parallel:

    1. %ms ~ms間 待機する ◎ Wait ms milliseconds.
    2. %v で %p を`解決する$ ◎ Resolve p with v.
  6. 事由~値 %r による %resolvedToPromise の`却下-時$には、次の手続きを`並列的$に遂行する: ◎ Upon rejection of resolvedToPromise with reason r, perform the following steps in parallel:

    1. %ms ~ms間 待機する ◎ Wait ms milliseconds.
    2. %r で %p を`却下する$ ◎ Reject p with r.
  7. ~RET %p ◎ Return p.

JavaScript による等価な関数は,次の様になるであろう: ◎ The equivalent function in JavaScript would be

function addDelay(%promise, %ms) {
    %ms = Number(%ms);
    %ms = Number.isNaN(%ms) ? +0 : Math.max(%ms, +0);

    let %resolve, %reject;
    const %p = new Promise((%r, %rr) `射$ { %resolve = %r; %reject = %rr; });

    const resolvedToPromise = Promise.resolve(%promise);
    resolvedToPromise.then(
        %v 射 setTimeout(() 射 %resolve(%v), %ms),
        %r 射 setTimeout(() 射 %reject(%r), %ms)
    );

    return %p;
}

6.4. `resource.open ( resourcePath, openingOperation )^c

`resource.open^c は、[ その働きのほとんどは,渡された関数 %openingOperation の実行-が占める ]が、しかる~~後,[ この演算の結果を反映するように `resource^c の~propertyたちを更新する ]ような,~methodである。 これは、[ Streams 仕様にて用いられている一部の技法 ]の,単純化された~versionである。 この~methodは、別の関数を `~promise-call$ する方法, その理由について説明0するために~~導入したものである。 ◎ resource.open is a method that executes the passed function openingOperation to do most of its work, but then updates the resource’s properties to reflect the result of this operation. It is a simplified version of some of the techniques used in the streams specification. The method is meant to illustrate how and why you might promise-call another function.

  1. %resourcePath ~LET ToString(%resourcePath) ◎ Let resourcePath be ToString(resourcePath).
  2. %openingPromise ~LET %openingOperation(%resourcePath) を`~promise-call$した結果 ◎ Let openingPromise be the result of promise-calling openingOperation(resourcePath).
  3. ~RET 次の~handlerで %openingPromise を`変形した$結果: ◎ Return the result of transforming openingPromise with:

    • [ `this.status^c ~SET `opened^l ]にするような,充足~handler。 ◎ A fulfillment handler that sets this.status to "opened".
    • 引数 %r を伴って~callされたときに[ `this.status^c ~SET `errored^l; `this.error^c ~SET %r ]にするような,却下~handler。 ◎ A rejection handler that, when called with argument r, set this.status to "errored" and this.error to r.

上に定義された `promiseCall^c 関数 を利用すれば、JavaScript による等価な関数は,次の様になるであろう: ◎ The equivalent function in JavaScript would be

resource.open = function (%resourcePath, %openingOperation) {
    %resourcePath = String(%resourcePath);

    return promiseCall(%openingOperation, %resourcePath).then(
        %v `射$ {
            this.status = "opened";
        },
        %r 射 {
            this.status = "errored";
            this.error = %r;
        }
    );
};

◎ using the promiseCall function defined above.

注記: 単に %openingOperation を~callしていないことに注意 — すなわち,直に %openingOperation(%resourcePath) を行うことにするなら、~codeは次の様になるが: ◎ Note how if we had instead just called openingOperation, i.e. done openingOperation(resourcePath) directly, then code like

resource.open(%synchronouslyOpenTheResource).then(%doSomethingElse);

それは~promiseを返さないので、返値~上で `then^c を~callするときに失敗することになる。 それを加味したとしても, %synchronouslyOpenTheResource が~errorを投出したときはどうするか? 結果が `errored^l ~statusになるように欲するが、`~promise-call$しなければ、その~errorにより,単純に `resource.open^c から抜出ることになってしまう。 ここでは、`~promise-call$が とても役立つことが見れる。 ◎ would fail. It would not return a promise, so calling then on the return value would fail. Even if we accounted for that, what if synchronouslyOpenTheResource threw an error? We would want that to result in an "errored" status, but without promise-calling, that would not be the case: the error would simply cause resource.open to exit. So you can see that promise-calling is quite helpful here.

6.5. %environment`.ready^c

ある “環境” を表現する `Environment^c ~obj %environment の `ready^c ~methodは、環境の何らかの部分 — 例えば,~DOM文書 — が “~ready(準備済み)” になったときに,それを通達する~propertyであるとする。 ここでは、その環境における非同時性について~~定式化する方法を説明0する: ◎ environment.ready is a property that signals when some part of some environment becomes "ready," e.g. a DOM document. It illustrates how to encode environmental asynchronicity.

  • どの `Environment^c ~objも,[ 新たな~promiseに初期化される, [[ready]] 内部slot ]を有するようにする。 %environment の `ready^c 取得子が~callされたときは、この `Environment^c ~objの [[ready]] 内部slotの値が返されるとする。 ◎ Let every Environment object have a [[ready]] internal slot, initialized with a new promise. When the getter for environment.ready is called, return the value of this Environment object’s [[ready]] internal slot.
  • 次の各~段が `Environment^c ~objを~readyにするような,ある~algoの末尾側に挿入されることになるであろう: ◎ The following steps might be inserted toward the end of some algorithm for readying Environment objects:

    • 当の環境が成功裡に~readyになったときは ⇒ `undefined^jv で[ `Environment^c ~obj の [[ready]] ~promise ]を`解決する$ ◎ If the environment becomes ready successfully, resolve this Environment object’s [[ready]] promise with undefined.
    • 当の環境が~readyになるのに失敗したときは ⇒ [ ~load失敗を説明する `Error^jv ~instance ]で[ `Environment^c ~obj の [[ready]] ~promise ]を`却下する$ ◎ If the environment fails to become ready, reject this Environment object’s [[ready]] promise with an Error instance explaining the load failure.

6.6. `addBookmark()^c

`addBookmark^c は、[ 現在の~web頁を~bookmarkとして追加する ]ことを利用者に要請する,関数である。 これは、 何度も行われる設計~作業 から取り出されたものであり、 環境上の非同時性を,現実にありそうな局面として解ってもらうと伴に、即時の却下についても説明0するものである。 ◎ addBookmark is a function that requests that the user add the current web page as a bookmark. It’s drawn from some iterative design work and illustrates a more real-world scenario of appealing to environmental asynchrony, as well as immediate rejections.

  1. ~IF[ この~methodは、明示的な利用者~動作の結果として,呼出された ]のではない ⇒ ~RET [ 名前 `SecurityError^l の新たな `DOMException$c ]で`却下される~promise$ ◎ If this method was not invoked as a result of explicit user action, return a promise rejected with a new DOMException whose name is "SecurityError".
  2. ~IF[ 文書の[ mode of operation† ] ~EQ standalone†† ] ⇒ ~RET [ 名前 `NotSupported^l の新たな `DOMException$c ]で`却下される~promise$ ◎ If the document’s mode of operation is standalone, return a promise rejected with a new DOMException whose name is "NotSupported".

    【† display mode, ††standalone ?】
  3. %promise ~LET 新たな~promise ◎ Let promise be a new promise.
  4. %info ~LET [ ~web~appの~metadata ]を得た結果 ◎ Let info be the result of getting a web application’s metadata.
  5. 次の手続きを`並列的$に走らせる: ◎ Run the following steps in parallel:

    1. %info を用いて, ~UAに特有の方式で,[ 末端利用者が~bookmarkを追加したいと欲するかどうか,選択する ]ことを許容する ◎ Using info, and in a manner that is user-agent specific, allow the end user to make a choice as to whether they want to add the bookmark.

      1. ~IF[ 末端利用者は ~bookmarkの追加-要請を中止した(利用者が~escapeキーを叩いた, “~cancel” ~buttonを押したなど) ] ⇒ [ 名前 `AbortError^l の新たな `DOMException$c ]で %promise を`却下する$ ◎ If the end-user aborts the request to add the bookmark (e.g., they hit escape, or press a "cancel" button), reject promise with a new DOMException whose name is "AbortError".
      2. ~ELSE ⇒ `undefined^jv で %promise を`解決する$ ◎ Otherwise, resolve promise with undefined.
  6. ~RET %promise ◎ Return promise.

6.7. `batchRequest( urls )^c

`SERVICE-WORKERS$r では、何箇所かで “`すべてを待機する$” が利用されている。 `batchRequest^c †は、その種のある利用を単純化した~versionを説明0する。 それは、一連の~URLを入力にとり,各~URLを~fetchして作成された `Response$c ~objからなる配列に対する~promiseを返す。 いずれかの~fetchが失敗した場合には、その失敗で`却下される~promise$を返す。 ◎ Several places in [SERVICE-WORKERS] use waiting for all. batchRequest illustrates a simplified version of one of their uses. It takes as input an iterable of URLs, and returns a promise for an array of Response objects created by fetching the corresponding URL. If any of the fetches fail, it will return a promise rejected with that failure.

【† `batchRequest^c は、現在の仕様には無い。 おそらく、別名の~method( `addAll^c ?)に置き換えられている。 】

  1. %応答~promiseたち ~LET 新たな空~list ◎ Let responsePromises be a new empty list
  2. %urls 内の~EACH ( 値 %url ) に対し: ◎ For each value url of urls,

    1. %url ~LET %url を `USVString$c に変換した結果 ◎ Let url be the result of converting url to a USVString.
    2. %req ~LET 新たな `Request$c を,その構築子に %url を渡して作成した結果 ◎ Let req be the result of creating a new Request passing url to the constructor.
    3. %p ~LET %req を引数に `fetch()$c を~callした結果 ◎ Let p be the result of calling fetch with req.
    4. %応答~promiseたち に %p を追加する ◎ Add p to responsePromises.
  3. ~RET %応答~promise~list の`すべてを待機-$した結果 ◎ Return the result of waiting for all of responsePromises.

7. WebIDL と~promise

この節は,主に仕様の書手~向けであり、~promiseを,~web仕様にてよく利用される~interface定義~言語と統合する方法について扱う。 ◎ This section is primarily for spec writers, dealing with how promises integrate with an interface definition language often used in web specs.

`WEBIDL$r は`~PromiseT$ 型を供する — それは WebIDL を通してそれらの~APIを公開するような仕様を書くときに,利用できる。 参照し易くするため、~PromiseT 型が及ぼす影響0を,ここに要約する。 ◎ [WEBIDL] provides a Promise<T> type which can be used when writing specifications that expose their API through WebIDL. We summarize the impact of Promise<T> here for easy reference.

7.1. ~PromiseT 型の返値

すべての WebIDL 返値と同様、返値として~PromiseT 型を宣言することは、~algoの値を返す実際の段には,影響0しない。 それは単純に書式上のものであり、[ ~promiseでない何か, または WebIDL %T 型でない充足~値による~promise ]を返すように仕様を書くことは、不正になる。 ◎ Like all WebIDL return values, declaring a return value of type Promise<T> has no impact on the algorithm’s actual return steps. It is simply a form of documentation, and if you return something that is not a promise or is a promise with a fulfillment value that is not of WebIDL-type T, then you have written incorrect documentation into your spec.

しかしながら、[ ~methodや~accessorを,~promiseを返すように宣言する ]ことには、一つの重要な影響0がある: それは、[ さもなければ — 例えば,型~変換に失敗した結果として — 投出されるような例外が、必ず~catchされ,却下される~promiseに転換される ]ようにする( WebIDL の “演算” 節 の “If O has a return type that is a promise type …” 【“〜の返値~型が~promise型であるならば…”】 の所, および その文書~~全体に散らばる,それに類する句を見よ)。 これにより、少なくとも例外については、[ ~promiseを返す関数は,常に~promiseを返すべきである ]原則は,自動的に守られることになる。 ◎ However, declaring that your method or accessor returns a promise does have one important impact: it ensures that any exceptions that it would otherwise throw, e.g. as a result of failed type conversions, are caught and turned into rejected promises. (See the "Operations" section, "If O has a return type that is a promise type …", and similar phrases scattered throughout the document.) This automatically takes care of the advice in §4.1.1 Promise-Returning Functions Should Always Return Promises, at least for exceptions.

7.2. ~PromiseT 型の~parameter

WebIDL ~methodの~parameterが ~PromiseT 型として宣言されているとき、その~parameterとして渡された どの引数も自動的に`~promiseとして解決され$、 ~promise引数は解決されるべきである 原則は守られることになる。 ◎ When a parameter of a WebIDL method is declared as Promise<T>, it will automatically resolve any arguments passed in that position. This will take care of the "Promise Arguments Should Be Resolved" advice above.

WebIDL ~PromiseT 型の引数が得られたなら、 WebIDL による “~promiseが決着したときの手続きを遂行する” ~algoを利用できる。 これは、上の `充足-時$には…/`却下-時$には… の略記~句とよく似るが、 “充足-時には” の手続きを走らす前に,[ ~promiseの充足~値を WebIDL 型 %T に変換する ]段も追加されている。 加えて、仕様の~algoが[ 前者/後者の手続きを走らせて導出される,~promise ]を返すようにもする。 型~変換に失敗した場合、~algoは[ それを失敗させた~error ]で却下される~promiseを返すようにする。 ◎ If you have a WebIDL Promise<T> argument, you can use the WebIDL perform some steps once a promise is settled algorithm. This is much like our upon fulfillment … and upon rejection … shorthand phrases above, but it will add an additional step of converting the promise’s fulfillment value to the WebIDL type T before running any upon-fulfillment steps. Additionally it causes your algorithm to return a promise derived from running those steps. If the type conversion fails, your algorithm will return a promise rejected with the error causing that failure.

ここでの %T は、 充足~値の WebIDL 型を指すことに注意。 更には、 WebIDL の “~promiseが`決着-$したときの手続きを遂行する” ~algoを利用する場合にのみ,影響0するものであり、他の仕方で~promiseを利用する場合(別の関数を通して それを渡すなど)は,そうならない。 型が何でもよければ、 `Promise<any>^c 型を~parameterに利用することを勧める。 ◎ Note that the T here refers to a WebIDL type for the fulfillment value. Furthermore, it only has impact if you use the WebIDL "perform some steps …" algorithm, and not if you use the promise in other ways (such as passing it along to another function). If that is not relevant, we advise using Promise<any> for parameters.

解決の挙動の帰結として、~PromiseT 型の~parameterは,他のいかなる~parameterとも`多重定義$し得ない。 例えば、次のように宣言することはできない: ◎ As a consequence of the resolution behavior, Promise<T> parameters cannot be overloaded with any other parameters. For example, you cannot do:

// 妥当でない WebIDL
void f(Promise<DOMString> %x);
void f(DOMString %y);

7.3. ~promiseを返す開発者~関数

WebIDL においては、`~callback関数$として宣言することにより(または、稀な事例では`~callback~interface$を介して)、 JavaScript 関数を~~引数にとれるようになる。 それは後に、 WebIDL 値の~listを~~引数に 呼出される。 ◎ In WebIDL, you consume JavaScript functions by declaring them as WebIDL callback functions (or, in rare cases, via callback interfaces) and later invoking them with a list of WebIDL values.

WebIDL の仕組みを利用して JavaScript 関数を~callすることにした場合、呼出~algoにおいては、自動的に返値を[ ~promiseとして解決する, あるいは 例外が投出されたなら却下される~promiseに変換する ]ことになり、[ ~promiseを返す関数として開発者から給された関数は “~promise-callされる” べきである ]原則は,自動的に守られることになる。 ◎ If you use WebIDL’s mechanisms for calling JavaScript functions, the invocation algorithm will automatically resolve return values and convert thrown exceptions into rejected promises. This automatically takes care of the advice in §4.3.2 Developer-Supplied Promise-Returning Functions Should Be "Promise-Called".

7.4. 例

~promiseを返す~method: ◎ Promise-returning methods:

interface ProtectedResource {
  Promise<void> requestAccess();
  // ...
};

interface Quoter {
  Promise<DOMString> getInterestingQuote();
};

~promiseを返す~property: ◎ Promise-returning properties

interface StateMachine {
  readonly attribute Promise<void> loaded;

  Promise<void> load();
};

~promiseを受容する~method: ◎ Promise-accepting methods

interface Waiter {
  void waitUntil(Promise<any> %promise);
};

~promiseを返す開発者~関数 ◎ Promise-returning developer functions

callback Promise<DOMString> ResourceLoader();

interface ResourceConsumer {
  void loadAndConsumeResource(ResourceLoader %loader);
};

付録: 旧来の非同期 API

多くの~web~platform API は、~promiseの到来より前に書かれているため、自前の場当たり的な仕方で,非同期的 演算の[ 完了/失敗 ]を通達している。 これらには次のものが含まれる: ◎ Many web platform APIs were written before the advent of promises, and thus came up with their own ad-hoc ways of signaling asynchronous operation completion or failure. These include:

【仕様の策定者は、】 これらに類似するような何かが少しでも見出されるなら、それは~~止めて,代わりに~promiseを利用するように。 ◎ If you find yourself doing something even remotely similar to these, stop, and instead use promises.