Skip to content

Commit

Permalink
2024/10/07 時点の英語版に基づき更新
Browse files Browse the repository at this point in the history
  • Loading branch information
mfuji09 committed Oct 13, 2024
1 parent 1d80e53 commit b93c74c
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 30 deletions.
58 changes: 30 additions & 28 deletions files/ja/web/javascript/reference/global_objects/promise/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Promise
slug: Web/JavaScript/Reference/Global_Objects/Promise
l10n:
sourceCommit: 10b342385644e822d123694ad3bc8c2ca9abb2dc
sourceCommit: 1b4e6d1156e8471d38deeea1567c35ef412c5f42
---

{{JSRef}}
Expand All @@ -13,7 +13,7 @@ l10n:

## 解説

プロミス (**`Promise`**) は、作成された時点では分からなくてもよい値へのプロキシーです。非同期のアクションの成功値または失敗理由にハンドラーを結びつけることができます。これにより、非同期メソッドは結果の値を返す代わりに、未来のある時点で値を提供する*プロミス*を返すことで、同期メソッドと同じように値を返すことができるようになります。
プロミス (`Promise`) は、作成された時点では分からなくてもよい値へのプロキシーです。非同期のアクションの成功値または失敗理由にハンドラーを結びつけることができます。これにより、非同期メソッドは結果の値を返す代わりに、未来のある時点で値を提供する*プロミス*を返すことで、同期メソッドと同じように値を返すことができるようになります。

`Promise` の状態は以下のいずれかとなります。

Expand Down Expand Up @@ -45,11 +45,11 @@ new Promise((resolveOuter) => {
> [!NOTE]
> Scheme など一部の言語では、遅延評価や計算を延期する機構を持っており、これらも「プロミス」と呼ばれます。 JavaScript におけるプロミスは、すでに起きつつある処理を表し、この処理はコールバック関数を使うことで連鎖させることができます。式を遅延評価する方法を探しているのであれば、引数なしの関数を使用するを検討してください。 `f = () => expression` で遅延評価される式が作成でき、 `f()` でその式を直ちに評価することができます。
### プロミスの連鎖
`Promise` 自体にはキャンセル用の第一級プロトコルはありませんが、通常は [`AbortController`](/ja/docs/Web/API/AbortController) を使用して、その基盤となる非同期操作を直接キャンセルできる場合があります。

{{jsxref("Promise.prototype.then()")}}, {{jsxref("Promise.prototype.catch()")}}, {{jsxref("Promise.prototype.finally()")}} の各メソッドを使用して、決定したプロミスにさらなるアクションを結びつけることができます。
### プロミスの連鎖

`.then()` メソッドは、最大で 2 つの引数を取ります。1 番目の引数は、プロミスが履行された場合のコールバック関数で、 2 番目の引数は、拒否された場合のコールバック関数です。それぞれの `.then()` は新たに生成されたプロミスオブジェクトを返します。このオブジェクトは、オプションで連鎖に使用することができます。例えば、このようになります
{{jsxref("Promise/then", "then()")}}, {{jsxref("Promise/catch", "catch()")}}, {{jsxref("Promise/finally", "finally()")}} の各メソッドを使用して、決定したプロミスにさらなるアクションを結びつけることができます。`then()` メソッドは、最大で 2 つの引数を取ります。1 番目の引数は、プロミスが履行された場合のコールバック関数で、 2 番目の引数は、拒否された場合のコールバック関数です。`catch()` メソッドと `finally()` メソッドは内部で `then()` を呼び出し、エラー処理を簡潔にします。例えば、`catch()` は履行ハンドラーを渡さない `then()` メソッドと同じです。これらのメソッドはプロミスを返すため、連結することができます。例えば次のようになります

```js
const myPromise = new Promise((resolve, reject) => {
Expand All @@ -64,9 +64,21 @@ myPromise
.then(handleFulfilledC, handleRejectedC);
```

`.then()` にコールバック関数がない場合でも、連鎖の次のリンクへと処理が進みます。したがって、連鎖では最後の `.catch()` まで、すべての*拒否*のコールバック関数を省略しても安全です。
ここでは、次の用語を使用します。「初期プロミス」とは、`then` が呼び出されるプロミスです。「新しいプロミス」とは、`then` が返すプロミスです。`then` に渡す 2 つのコールバックは、それぞれ「履行ハンドラー」と「拒否ハンドラー」と呼びます。

初期プロミスの決定状態によって、実行するハンドラーが決まります。

- 初期プロミスが履行された場合、履行ハンドラーが履行値とともに呼び出されます。
- 初期プロミスが拒否された場合、拒否ハンドラーが拒否理由とともに呼び出されます。

ハンドラーの完了によって、新しいプロミスの決定状態が決まります。

- ハンドラーが [Thenable](#thenable) 値を返した場合、新しいプロミスは返値と同じ状態で決定されます。
- ハンドラーが Thenable ではない値を返した場合、新しいプロミスは返値で履行されます。
- ハンドラーがエラーを発生させた場合、新しいプロミスは発生したエラーで拒否されます。
- 初期プロミスに該当するハンドラーが接続されていない場合、新しいプロミスは初期プロミスと同じ状態に決定されます。つまり、拒否ハンドラーがなければ、拒否されたプロミスは同じ理由で拒否されたままになります。

それぞれの `.then()` で拒否されたプロミスを扱うと、プロミスの連鎖のさらに先に影響を及ぼします。エラーを直ちに処理しなければならないため、選択の余地がない場合もあります。このような場合には、連鎖的にエラー状態を維持するために、何らかの種類のエラーを発生させる必要があります。一方で、緊急の必要性がない場合は、最後の `.catch()` ステートメントまでエラー処理を行わない方がシンプルです。 `.catch()` は、実際には、単なる `.then()` からプロミスが履行されたときのためのコールバック関数のスロットを抜いたものです
例えば、上記のコードでは、`myPromise` が拒否された場合、`handleRejectedA` が呼び出され、`handleRejectedA` が正常に完了した場合(エラーが発生したり拒否されたプロミスを返したりしない限り)、最初の `then` で返されたプロミスは拒否されたままになるのではなく、履行されます。したがって、エラーをすぐに処理する必要があるものの、その後の処理でエラー状態を維持したい場合は、拒否ハンドラーで何らかの型のエラーを発生させる必要があります。一方、即座にエラー処理をする必要がない場合は、最終的な `catch()` ハンドラーまでエラー処理を省略する方が簡単です

```js
myPromise
Expand Down Expand Up @@ -95,29 +107,17 @@ myPromise
> [!NOTE]
> より高速に実行するためには、できればすべての同期アクションを 1 つのハンドラー内で行うようにしてください。そうしなければ、すべてのハンドラーを順番に実行するのに数カウントかかることになります。
プロミスの終了条件は、その連鎖内の次のプロミスの「決定」状態を決定します。「履行」状態はプロミスの完了が成功したことを示し、「拒否」状態は成功しなかったことを示します。連鎖内で履行されたそれぞれのプロミスの返値は、次の `.then()` に渡され、拒否された理由は連鎖内の次の拒否ハンドラー関数に渡されます。

連鎖のプロミスは、他のものの中に入れ子になっていますが、スタックの一番上のように取り出されます。連鎖の最初のプロミスは最も深くネストされており、最初に取り出されます。

```plain
(プロミス D, (プロミス C, (プロミス B, (プロミス A) ) ) )
```

`nextValue` がプロミスである場合、その効果は動的な置換です。 `return` によってプロミスが取り出されますが、 `nextValue` のプロミスはその場所に押し込まれます。上に示した入れ子では、"プロミス B" に関連付けられた `.then()` が "プロミス X" の `nextValue` を返すとします。 結果としての入れ子は以下のようになります。

```plain
(プロミス D, (プロミス C, (プロミス X) ) )
```
JavaScript は[ジョブキュー](/ja/docs/Web/JavaScript/Event_loop)を維持します。 JavaScript は毎回、キューからジョブを選択し、それを完全に実行します。毎回、JavaScript でキューからジョブが選択され、完全に実行されます。 ジョブは、`Promise()` コンストラクターの実行者、`then` に渡されたハンドラー、またはプロミスを返すプラットフォーム API によって定義されます。 連鎖するプロミスは、これらのジョブ間の依存関係を表します。 プロミスが確定すると、それに関連付けられた各ハンドラーがジョブキューの最後に追加されます。

プロミスは複数の入れ子に参加することができます。以下のコードでは、 `プロミス A` が「決定」状態に移行すると、 `.then()` の両方のインスタンスが呼び出されます
プロミスは複数の連鎖に参加できます。次のコードにおいて、`promiseA` が履行されると、`handleFulfilled1``handleFulfilled2` の両方がジョブキューに追加されます。`handleFulfilled1` が最初に登録されているため、最初に呼び出されます

```js
const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);
```

既に「決定」状態のプロミスにアクションを割り当てることができます。その場合、アクションは (適切であれば) 最初の非同期の機会に実行されます。プロミスは非同期であることが保証されていることに注意してください。したがって、既に「解決」状態のプロミスに対するアクションは、スタックがクリアされ、クロックティックが経過した後にのみ実行されます。この効果は `setTimeout(action, 0)` とよく似ています
すでに決定済みのプロミスにアクションを割り当てることができます。この場合、アクションはすぐにジョブキューの最後に追加され、既存のジョブがすべて完了すると実行されます。したがって、すでに「決定済み」のプロミスに対するアクションは、現在の同期コードが完了し、少なくとも1つのループティックが渡された後にのみ発生します。これにより、プロミスアクションが非同期であることが保証されます。

```js
const promiseA = new Promise((resolve, reject) => {
Expand All @@ -142,7 +142,7 @@ JavaScript のエコシステムには、プロミスが言語の一部となる
const aThenable = {
then(onFulfilled, onRejected) {
onFulfilled({
// The thenable is fulfilled with another thenable
// Thenable は他の Thenable で履行される
then(onFulfilled, onRejected) {
onFulfilled(42);
},
Expand Down Expand Up @@ -177,7 +177,7 @@ JavaScript はもともと[シングルスレッド](/ja/docs/Glossary/Thread)

## 静的プロパティ

- {{jsxref("Promise/@@species", "Promise[@@species]")}}
- [`Promise[Symbol.species]`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/Symbol.species)
- : プロミスのメソッドから返値を構築するために使用するコンストラクターを返します。

## 静的メソッド
Expand All @@ -194,6 +194,8 @@ JavaScript はもともと[シングルスレッド](/ja/docs/Glossary/Thread)
- : 与えられた理由で拒否された新しい `Promise` オブジェクトを返します。
- {{jsxref("Promise.resolve()")}}
- : 与えられた値で解決された `Promise` オブジェクトを返します。もし値が Thenable (つまり `then` メソッドを持っているオブジェクト)ならば、返されるプロミスはその Thenable をたどり、その結果を採用します。そうでなければ、返されるプロミスは与えられた値で履行されます。
- {{jsxref("Promise.try()")}} {{experimental_inline}}
- : あらゆる種類のコールバック(復帰か例外か、同期的か非同期的にかかわらず)を取り、その結果を `Promise` でラップします。
- {{jsxref("Promise.withResolvers()")}}
- : {{jsxref("Promise/Promise", "Promise()")}} コンストラクターの実行側に渡された 2 つの引数に対応する、新しい `Promise` オブジェクトとそれを解決または拒否する 2 つの関数を格納したオブジェクトを返します。

Expand All @@ -203,8 +205,8 @@ JavaScript はもともと[シングルスレッド](/ja/docs/Glossary/Thread)

- {{jsxref("Object/constructor", "Promise.prototype.constructor")}}
- : インスタンスオブジェクトを作成したコンストラクター関数。 `Promise` インスタンスの場合、初期値は {{jsxref("Promise/Promise", "Promise")}} コンストラクターです。
- `Promise.prototype[@@toStringTag]`
- : [`@@toStringTag`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) プロパティの初期値は文字列 `"Promise"` です。このプロパティは {{jsxref("Object.prototype.toString()")}} で使用されます。
- `Promise.prototype[Symbol.toStringTag]`
- : [`[Symbol.toStringTag]`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) プロパティの初期値は文字列 `"Promise"` です。このプロパティは {{jsxref("Object.prototype.toString()")}} で使用されます。

## インスタンスメソッド

Expand Down Expand Up @@ -305,7 +307,7 @@ new Promise(tetheredGetNumber)

### 高度な例

以下の例は `Promise` の仕組みを示したものです。 `testPromise()` メソッドは {{HTMLElement("button")}} をクリックする度に呼び出されます。これは、{{domxref("setTimeout()")}} を用いて、 1 秒から 3 秒のランダムな時間の後、メソッドがこれまでに呼ばれた回数で履行されるプロミスを作成します。 `Promise()` コンストラクターを使用してプロミスを生成します。
以下の例は `Promise` の仕組みを示したものです。 `testPromise()` メソッドは {{HTMLElement("button")}} をクリックする度に呼び出されます。これは、{{domxref("Window.setTimeout", "setTimeout()")}} を用いて、 1 秒から 3 秒のランダムな時間の後、メソッドがこれまでに呼ばれた回数で履行されるプロミスを作成します。 `Promise()` コンストラクターを使用してプロミスを生成します。

プロミスが履行されたことは、 {{jsxref("Promise/then", "p1.then()")}} で設定されたコールバックによって記録されます。この記録から、メソッドの同期処理部分が、プロミスによる非同期処理からどのように分離されているかがわかります。

Expand Down Expand Up @@ -457,4 +459,4 @@ btn.addEventListener("click", testPromise);
- [プロミスの使用](/ja/docs/Web/JavaScript/Guide/Using_promises)ガイド
- [Promises/A+ specification](https://promisesaplus.com/)
- [JavaScript Promises: an introduction](https://web.dev/articles/promises) (web.dev, 2013)
- [Callbacks, Promises, and Coroutines: Asynchronous Programming Patterns in JavaScript](https://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript) Domenic Denicola によるスライドショー (2011)
- [Callbacks, Promises, and Coroutines: Asynchronous Programming Patterns in JavaScript](https://www.slideshare.net/slideshow/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript/9953720) (Domenic Denicola によるスライドショー, 2011)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Promise.race()
slug: Web/JavaScript/Reference/Global_Objects/Promise/race
l10n:
sourceCommit: 3f0cd840cd9575701c65b8c6a1e172a2b0c3bd62
sourceCommit: 1b4e6d1156e8471d38deeea1567c35ef412c5f42
---

{{JSRef}}
Expand Down Expand Up @@ -39,7 +39,7 @@ Promise.race(iterable)

### Promise.race() の使用

この例では、 `Promise.race()` を使用して、 [`setTimeout()`](/ja/docs/Web/API/setTimeout) で実装された複数のタイマーを競わせることができることを示しています。最も時間の短いタイマーが常にレースに勝ち、結果のプロミスの状態となります。
この例では、 `Promise.race()` を使用して、{{domxref("Window.setTimeout", "setTimeout()")}} で実装された複数のタイマーを競わせることができることを示しています。最も時間の短いタイマーが常にレースに勝ち、結果のプロミスの状態となります。

```js
function sleep(time, value, state) {
Expand Down

0 comments on commit b93c74c

Please sign in to comment.