From 8c68b0bd6f8565c51416ec3f6ce4e78a32d014fc Mon Sep 17 00:00:00 2001
From: Masahiro FUJIMOTO The メモ: この関数は、主に URL クエリ (URL の 文字列リテラル内のエスケープされた文字は、 特定の文字がエスケープされた新しい文字列。 文字の16進数形式として、文字コードの値が {{Compat("javascript.builtins.escape")}} プロミスの挙動と使用法について学ぶには、最初に Promise の使用をお読みください。 待機状態のプロミスは、何らかの値を持つ満足 (fulfilled) 状態、もしくは何らかの理由 (エラー) を持つ拒絶 (rejected) 状態のいずれかに変わります。そのどちらとなっても、 混乱を避けるために: Scheme に代表されるいくつかの言語では、遅延評価や計算を延期する機構を持っており、これらも "Promise" と呼ばれます。 JavaScript における Promise は、すでに起きつつある処理を表します。そしてこの処理はコールバックを使うことでチェーンさせることができます。式を遅延評価する方法を探しているのであれば、引数なしのアロー関数を考えてください。 注: Promise は fulfilled か failed のどちらかになった場合は、 pending ではなく settled と呼ばれます。また解決 (resolved) という用語も目にされたことがあると思います。解決とは、Promise が解決または他の promise の状態にマッチするために" locked in "したことを意味します。States and fates では、 Promise の技術についてより詳細に述べられています。 拒絶されたプロミスの処理が早すぎると、プロミスの連鎖のさらに下の方に影響を及ぼします。エラーはすぐに処理しなければならないので、選択の余地がないこともあります。 (結果を処理するためのテクニックについては、下記の例の これら2つの関数のシグネチャはシンプルで、任意の型の引数を1つだけ受け取ることができます。これらの関数を書くのはプログラマーです。これらの関数の終了条件は、チェーン内の次のプロミスの「解決」状態を決定します。 返される プロミスのチェーンはロシアの人形のように入れ子にすることができますが、スタックの最上位のように取り出します。チェーンの最初のプロミスは最も深いところに入れ子になり、最初に取り出されます。 プロミスは複数の入れ子に参加することができます。以下のコードでは、 既に「解決済み」のプロミスにアクションを割り当てることができます。その場合、アクションは (適切であれば) 最初の非同期の機会に実行されます。プロミスは非同期であることが保証されていることに注意してください。したがって、既に「解決済み」のプロミスに対するアクションは、スタックがクリアされ、クロックティックが経過した後にのみ実行されます。この効果は この例では、プロミス機能を使用するための多様なテクニックと、発生する可能性のある多様な状況を示しています。これを理解するには、まずコードブロックの一番下までスクロールして、プロミスの連鎖を調べてください。最初のプロミスが提供されると、プロミスの連鎖が続きます。このチェーンは 関数 関数 このコードは NodeJS で実行できます。実際にエラーが発生しているのを見ることで理解度が高まります。より多くのエラーを強制的に発生させるには、 以下の例は プロミスが満足したことは、 {{JSxRef("Promise.prototype.then()","p1.then()")}} で設定されたコールバックによって記録されます。この記録から、メソッドの同期処理部分が、 Promise による非同期処理からどのように分離されているかがわかります。 この例はボタンをクリックすると実行されます。 (ブラウザーが 短い時間の間に何度かボタンをクリックすると、それぞれの promise が次々と成功するのがわかります。 {{EmbedLiveSample("Advanced_Example", "500", "200")}} {{Compat("javascript.builtins.Promise")}}escape() は厳密には非推奨ではありませんが (「ウェブ標準から削除された」という意味で)、ECMA-262 標準の Annex B で定義されており、その導入部には次のように書かれています。
-
-… この付属書で規定されているすべての言語機能と動作は、1つ以上の望ましくない特性を持ち、レガシーな使用例がない場合は、この仕様から削除されます。…
-
-… プログラマは、新しい ECMAScript コードを書くときに、これらの機能や動作の存在を使用したり、仮定したりしてはいけません。…escape() 関数は、特定の文字を16進数のエスケープシーケンスで置き換えた新しい文字列を計算します。? に続く部分) に使われていました。 — "\xHH" の形式を使用して、ふつうの文字列リテラルをエスケープするためのものではありません。 (HHは2桁の16進数であり、より高い面の Unicode 文字には「\xHH\xHHxHH」という形式が使われます。)\x を % に置き換えてから、 decodeURIComponent() 関数を使用することで展開することができます。構文
-
-escape(str)
-
-引数
-
-
-
-
-str返値
-
-解説
-
-escape 関数はグローバルオブジェクトのプロパティです。特殊文字は @*_+-./ 以外の文字が符号化されます。0xFF 以下になる文字は 2 桁のエスケープシーケンス %xx が、それ以上の場合は 4 桁のエスケープシーケンス %uxxxx%uxxxx が使われます。例
-
-escape('abc123'); // "abc123"
-escape('äöü'); // "%E4%F6%FC"
-escape('ć'); // "%u0107"
-
-// 特殊文字
-escape('@*_+-./'); // "@*_+-./"
-
-仕様書
-
-
-
-
-
-
-
-
-
- 仕様書
-
-
-
-{{SpecName('ESDraft', '#sec-escape-string', 'escape')}}
- ブラウザーの互換性
-
-関連情報
-
-
-
diff --git a/files/ja/web/javascript/reference/global_objects/escape/index.md b/files/ja/web/javascript/reference/global_objects/escape/index.md
new file mode 100644
index 0000000000..5546f806fd
--- /dev/null
+++ b/files/ja/web/javascript/reference/global_objects/escape/index.md
@@ -0,0 +1,70 @@
+---
+title: escape()
+slug: Web/JavaScript/Reference/Global_Objects/escape
+tags:
+ - Deprecated
+ - JavaScript
+ - メソッド
+browser-compat: javascript.builtins.escape
+translation_of: Web/JavaScript/Reference/Global_Objects/escape
+---
+{{jsSidebar("Objects")}}
+
+> **Warning:** `escape()` は厳密には (「ウェブ標準から削除された」という意味で) 非推奨ではありませんが、ECMA-262 標準の [Annex B](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-additional-ecmascript-features-for-web-browsers) で定義されており、その導入部には次のように書かれています。
+>
+> > … この付録で規定されているすべての言語機能と動作は、 1 つ以上の望ましくない特性を持ち、レガシーな使用例がない場合は、この仕様から削除されます。…
+> … プログラマーは、新しい ECMAScript のコードを書くときに、これらの機能や動作の存在を使用したり、仮定したりしてはいけません。…
+
+The **`escape()`** 関数は、特定の文字を 16 進数のエスケープシーケンスで置き換えた新しい文字列を計算します。
+
+> **Note:** この関数は、主に URL クエリー (URL の `?` に続く部分) に使われていました。 — "`\xHH`" の形式を使用して、ふつうの文字列リテラルをエスケープするためのものでは*ありません*。 (HH は 2 桁の 16 進数であり、より高い面の Unicode 文字には「\xHH\xHHxHH」という形式が使われます。)
+>
+> 文字列リテラル内のエスケープされた文字は、 `\x` を `%` に置き換えてから、 `decodeURIComponent()` 関数を使用することで展開することができます。
+
+## 構文
+
+```js
+escape(str)
+```
+
+### 引数
+
+- `str`
+ - : エンコードする文字列。
+
+### 返値
+
+特定の文字がエスケープされた新しい文字列。
+
+## 解説
+
+`escape` 関数は*グローバルオブジェクト*のプロパティです。特殊文字は `@*_+-./` 以外の文字が符号化されます。
+
+文字の 16 進数形式として、文字コードの値が `0xFF` 以下になる文字は 2 桁のエスケープシーケンス `%xx` が、それ以上の場合は 4 桁のエスケープシーケンス `%uxxxx` が使われます。
+
+## 例
+
+### escape の使用
+
+```js
+escape('abc123'); // "abc123"
+escape('äöü'); // "%E4%F6%FC"
+escape('ć'); // "%u0107"
+
+// 特殊文字
+escape('@*_+-./'); // "@*_+-./"
+```
+
+## 仕様書
+
+{{Specifications}}
+
+## ブラウザーの互換性
+
+{{Compat}}
+
+## 関連情報
+
+- {{jsxref("encodeURI")}}
+- {{jsxref("encodeURIComponent")}}
+- {{jsxref("unescape")}}
--
cgit v1.2.3-54-g00ecf
From 90afa576ae0d1a7cfbca098e5a476f1fcffca663 Mon Sep 17 00:00:00 2001
From: Masahiro FUJIMOTO Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。解説
-
-Promise インターフェイスは作成時点では分からなくてもよい値へのプロキシです。 Promise を用いることで、非同期アクションの成功や失敗に対するハンドラーを関連付けることができます。これにより、非同期メソッドは、最終的な値を返すのではなく、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになります。Promise の状態は以下のいずれかとなります。
-
-
-then メソッドによって関連付けられたハンドラーが呼び出されます。 (対応するハンドラーが割り当てられたとき、既にプロミスが成功または失敗していても、そのハンドラーは呼ばれます。よって、非同期処理とその関連付けられたハンドラーとの競合は発生しません。){{JSxRef("Promise.then", "Promise.prototype.then()")}} メソッドと {{JSxRef("Promise.catch", "Promise.prototype.catch()")}} メソッドもまた Promise を返すので、これらをチェーン (連鎖) させることができます。
f = () => expression のように実現でき、遅延評価される式が作成され、 f() を呼ぶことでその式を評価できます。連鎖したプロミス
-
-promise.then(), promise.catch(), promise.finally() の各メソッドは、決定したプロミスにさらなるアクションを関連付けるために使用されます。これらのメソッドはまた、新しく生成されたプロミスオブジェクトを返します。例えば、このようになります。
-
-
-const myPromise =
- (new Promise(myExecutorFunc))
- .then(handleFulfilledA,handleRejectedA)
- .then(handleFulfilledB,handleRejectedB)
- .then(handleFulfilledC,handleRejectedC);
-
-// または、おそらく次の方がよい ...
-
-const myPromise =
- (new Promise(myExecutorFunc))
- .then(handleFulfilledA)
- .then(handleFulfilledB)
- .then(handleFulfilledC)
- .catch(handleRejectedAny);
- throw -999 を参照してください。) 一方で、すぐに必要がない場合は、最後の .catch() 文までエラー処理をしない方がシンプルです。throw 以外の終了条件は "解決" の状態を生み出し、一方、 throw で終了すると "拒否" の状態を生み出します。handleFulfilled(value) { /*...*/; return nextValue; }
-handleRejection(reason) { /*...*/; throw nextReason; }
-handleRejection(reason) { /*...*/; return nextValue; }
-
-nextValue は、別のプロミスオブジェクトにすることもでき、この場合はプロミスは動的にチェーンに挿入されます。.then() が適切な関数欠いている場合、処理は単純にチェーンの次のリンクへと続きます。したがってチェーンは、すべての handleRejection を最後の .catch() まで、安全に省略することができます。 同様に、.catch() は、実際には handleFulfilled 用のスロットのないただの .then() です。(promise D, (promise C, (promise B, (promise A) ) ) )
-
-nextValue がプロミスである場合、その効果は動的な置換です。 return によってプロミスが取り出されますが、 nextValue のプロミスはその場所に押し込まれます。上に示した入れ子では、"promise B" に関連付けられた .then() が "promise X" の nextValue を返すとします。 結果としての入れ子は以下のようになります。(promise D, (promise C, (promise X) ) )
-
-promiseA が「確定」状態に移行すると、 .then() の両方のインスタンスが呼び出されます。const promiseA = new Promise(myExecutorFunc);
-const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
-const promiseC = promiseA.then(handleFulfilled2, handleRejected2);
-
-
-setTimeout(action,10) とよく似ていますconst promiseA = new Promise( (resolutionFunc,rejectionFunc) => {
- resolutionFunc(777);
-});
-// この時点で、 "promiseA" はすでに解決されています。
-promiseA.then( (val) => console.log("asynchronous logging has val:",val) );
-console.log("immediate logging");
-
-// 以下の順序で出力が行われます。
-// immediate logging
-// asynchronous logging has val: 777
-
-
-コンストラクター
-
-
-
-
-Promise オブジェクトを生成します。このコンストラクターは主にまだプロミスに対応していない関数をラップするために使われます。静的メソッド
-
-
-
-
-Promise オブジェクトを返します。Promise オブジェクトを返します。もし値が thenable (つまり then メソッドを持っているオブジェクト) ならば、返される Promise はその thenable をたどり、その結果を採用します。そうでなければ、返される Promise は与えられた値で解決されます。インスタンスメソッド
-
-
-
-
-onFulfilled や onRejected が関数でない場合) には、元の完了した値に解決しているプロミスを返します。例
-
-基本的な使用例
-
-let myFirstPromise = new Promise((resolve, reject) => {
- // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
- // In this example, we use setTimeout(...) to simulate async code.
- // In reality, you will probably be using something like XHR or an HTML5 API.
- setTimeout( function() {
- resolve("Success!") // Yay! Everything went well!
- }, 250)
-})
-
-myFirstPromise.then((successMessage) => {
- // successMessage is whatever we passed in the resolve(...) function above.
- // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
- console.log("Yay! " + successMessage)
-});
-
-
-
-多様な状況に対応した例
-
-.then() の呼び出しで構成され、通常は (必ずしもそうとは限りませんが) 最後に単一の .catch() があり、オプションで .finally() が続きます。この例では、プロミスチェーンはカスタムで書かれた new Promise() コンストラクターによって開始されますが、実際には、プロミスチェーンは通常、プロミスを返す API 関数 (他の誰かが書いたもの) から開始されます。tetheredGetNumber() の例では、非同期呼び出しを設定している間、またはコールバック内で、またはその両方で reject() を使用してプロミスを生成することを示しています。 関数 promiseGetWord() は、API 関数がどのように自己完結型の方法でプロミスを生成して返すかを示しています。troubleWithGetNumber() は throw() で終わることに注意してください。これは、 ES6 のプロミスチェーンでは、エラーが発生した後で、 "throw()" がなく、エラーが "fixed" であるようにみえても、すべての .then() のプロミスを通過するため、強制的に行われています。これは面倒なので、 .then() プロミスのチェーン全体で rejectionFunc を省略して、最終的な catch() で単一の rejectionFunc を使用するのが一般的です。 別の方法としては、特別な値を投げるという方法があります (この場合は"-999" ですが、カスタムのエラー種別の方が適切です)。threshold の値を変更します。"use strict";
-
-// To experiment with error handling, "threshold" values cause errors randomly
-const THRESHOLD_A = 8; // can use zero 0 to guarantee error
-
-function tetheredGetNumber(resolve, reject) {
- try {
- setTimeout(
- function() {
- const randomInt = Date.now();
- const value = randomInt % 10;
- try {
- if(value >= THRESHOLD_A) {
- throw new Error(`Too large: ${value}`);
- }
- } catch(msg) {
- reject(`Error in callback ${msg}`);
- }
- resolve(value);
- return;
- }, 500);
- // To experiment with error at set-up, uncomment the following 'throw'.
- // throw new Error("Bad setup");
- } catch(err) {
- reject(`Error during setup: ${err}`);
- }
- return;
-}
-
-function determineParity(value) {
- const isOdd = value % 2 ? true : false ;
- const parityInfo = { theNumber: value, isOdd: isOdd };
- return parityInfo;
-}
-
-function troubleWithGetNumber(reason) {
- console.error(`Trouble getting number: ${reason}`);
- throw -999; // must "throw" something, to maintain error state down the chain
-}
-
-function promiseGetWord(parityInfo) {
- // The "tetheredGetWord()" function gets "parityInfo" as closure variable.
- var tetheredGetWord = function(resolve,reject) {
- const theNumber = parityInfo.theNumber;
- const threshold_B = THRESHOLD_A - 1;
- if(theNumber >= threshold_B) {
- reject(`Still too large: ${theNumber}`);
- } else {
- parityInfo.wordEvenOdd = parityInfo.isOdd ? 'odd' : 'even';
- resolve(parityInfo);
- }
- return;
- }
- return new Promise(tetheredGetWord);
-}
-
-(new Promise(tetheredGetNumber))
- .then(determineParity,troubleWithGetNumber)
- .then(promiseGetWord)
- .then((info) => {
- console.log("Got: ",info.theNumber," , ", info.wordEvenOdd);
- return info;
- })
- .catch((reason) => {
- if(reason === -999) {
- console.error("Had previously handled error");
- }
- else {
- console.error(`Trouble with promiseGetWord(): ${reason}`);
- }
- })
- .finally((info) => console.log("All done"));
-
-
-
-応用例
-
-<button id="btn">Make a promise!</button>
-<div id="log"></div>
-
-Promise の仕組みを示したものです。 testPromise() メソッドは {{HTMLElement("button")}} をクリックする度に呼び出されます。testPromise() メソッドは、 {{domxref("window.setTimeout()")}} を用いて、1秒から 3秒のランダムな時間の後、メソッドがこれまでに呼ばれた回数で成功する Promise を作成します。 Promise() コンストラクターは Promise を作成するために使用されます。'use strict';
-var promiseCount = 0;
-
-function testPromise() {
- let thisPromiseCount = ++promiseCount;
-
- let log = document.getElementById('log');
- log.insertAdjacentHTML('beforeend', thisPromiseCount +
- ') 開始 (<small>同期処理開始</small>)<br/>');
-
- // 新しい Promise を作成: 1~3秒後に結果を返すことを約束します
- let p1 = new Promise(
- // executor 関数は Promise の成功または失敗に応じて呼ばれます
- //
- (resolve, reject) => {
- log.insertAdjacentHTML('beforeend', thisPromiseCount +
- ') Promise 開始 (<small>非同期処理開始</small>)<br/>');
- // 非同期を作成するための一例です
- window.setTimeout(
- function() {
- // 約束を果たしました!
- resolve(thisPromiseCount);
- }, Math.random() * 2000 + 1000);
- }
- );
-
- // Promise が成功した時に何をするかを定めます then() で成功した時
- // catch() で失敗した時
- p1.then(
- // メッセージと値を記録します
- function(val) {
- log.insertAdjacentHTML('beforeend', val +
- ') Promise 成功 (<small>非同期処理終了</small>)<br/>');
- }).catch(
- // 失敗した理由を記録します
- (reason) => {
- console.log('Handle rejected promise ('+reason+') here.');
- });
-
- log.insertAdjacentHTML('beforeend', thisPromiseCount +
- ') Promise は作成されました (<small>同期処理終了</small>)<br/>');
-}
-
-if ("Promise" in window) {
- let btn = document.getElementById("btn");
- btn.addEventListener("click",testPromise);
-} else {
- log = document.getElementById('log');
- log.innerHTML = "Live example not available as your browser doesn't support the <code>Promise<code> interface.";
-}
-
-Promise に対応している必要があります。)XHR による画像の読み込み
-
-Promise と {{domxref("XMLHttpRequest")}} で画像を読み込む別の例は、 MDN GitHub js-examples リポジトリにあり、動作を確認することができます。それぞれの行のコメントで Promise と XHR の構造がよくわかるはずです。仕様書
-
-
-
-
-
-
-
- 仕様書
-
-
-
-{{SpecName('ESDraft', '#sec-promise-objects', 'Promise')}}
- ブラウザーの互換性
-
-関連情報
-
-
-
diff --git a/files/ja/web/javascript/reference/global_objects/promise/index.md b/files/ja/web/javascript/reference/global_objects/promise/index.md
new file mode 100644
index 0000000000..821495840f
--- /dev/null
+++ b/files/ja/web/javascript/reference/global_objects/promise/index.md
@@ -0,0 +1,435 @@
+---
+title: Promise
+slug: Web/JavaScript/Reference/Global_Objects/Promise
+tags:
+ - クラス
+ - ECMAScript 2015
+ - JavaScript
+ - Promise
+ - Reference
+ - promise.all
+ - Polyfill
+browser-compat: javascript.builtins.Promise
+translation_of: Web/JavaScript/Reference/Global_Objects/Promise
+---
+{{JSRef}}
+
+**`Promise`** オブジェクトは、非同期処理の完了 (もしくは失敗) の結果およびその結果の値を表します。
+
+{{AvailableInWorkers}}
+
+プロミスの挙動と使用法について学ぶには、最初に[プロミスの使用](/ja/docs/Web/JavaScript/Guide/Using_promises)をお読みください。
+
+## 解説
+
+プロミス (**`Promise`**) は、作成された時点では分からなくてもよい値へのプロキシーです。非同期のアクションの成功値または失敗理由にハンドラーを結びつけることができます。これにより、非同期メソッドは結果の値を返す代わりに、未来のある時点で値を提供する*プロミス*を返すことで、同期メソッドと同じように値を返すことができるようになります。
+
+`Promise` の状態は以下のいずれかとなります。
+
+- 待機 (_pending_): 初期状態。成功も失敗もしていません。
+- 履行 (_fulfilled_): 処理が成功して完了したことを意味します。
+- 拒否 (_rejected_): 処理が失敗したことを意味します。
+
+待機状態のプロミスは、何らかの値を持つ履行 (_fulfilled_) 状態、もしくは何らかの理由 (エラー) を持つ拒否 (_rejected_) 状態のいずれかに変わります。そのどちらとなっても、`then` メソッドによって関連付けられたハンドラーが呼び出されます。対応するハンドラーが割り当てられたとき、既にプロミスが履行または拒否状態になっていても、そのハンドラーは呼び出されます。よって、非同期処理とその関連付けられたハンドラーとの競合は発生しません。
+
+`{{JSxRef("Promise.then", "Promise.prototype.then()")}}` メソッドと `{{JSxRef("Promise.catch", "Promise.prototype.catch()")}}` メソッドもまたプロミスを返すので、これらを連鎖 (chain) させることができます。
+
+
+
+> **Note:** Scheme に代表される一部の言語では、遅延評価や計算を延期する機構を持っており、これらも「プロミス」と呼ばれます。 JavaScript におけるプロミスは、すでに起きつつある処理を表し、この処理はコールバックを使うことで連鎖させることができます。式を遅延評価する方法を探しているのであれば、引数なしのアロー関数を検討してください。 `f = () => expression` で遅延評価される式が作成でき、 `f()` を実行することで評価を実行することができます。
+
+> **Note:** プロミスは履行状態または拒否状態になった場合は、待機ではなく決定 (_settled_) と呼ばれます。また解決 (_resolved_) という用語も見かけるでしょう。これはプロミスが決定したか、他のプロミスの状態に一致させるために「ロックイン」したことを表します。 [States and fates](https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md) には、プロミスの用語についての詳細説明があります。
+
+### 複数のプロミスの連鎖
+
+`promise.then()`, `promise.catch()`, `promise.finally()` の各メソッドは、決定したプロミスにさらなるアクションを結びつけるために使用します。
+
+`.then()` メソッドは、最大で 2 つの引数を取ります。1 番目の引数は、プロミスが解決した場合のコールバック関数で、 2 番目の引数は、拒否された場合のコールバック関数です。それぞれの `.then()` は新たに生成されたプロミスオブジェクトを返します。このオブジェクトは、オプションで連鎖に使用することができます。例えば、このようになります。
+
+```js
+const myPromise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve('foo');
+ }, 300);
+});
+
+myPromise
+ .then(handleResolvedA, handleRejectedA)
+ .then(handleResolvedB, handleRejectedB)
+ .then(handleResolvedC, handleRejectedC);
+```
+
+`.then()` に Promise オブジェクトを返すコールバック関数がない場合でも、連鎖の次のリンクへと処理が進みます。したがって、連鎖では最後の `.catch()` まで、すべての*拒否*のコールバック関数を省略しても安全です。
+
+それぞれの `.then()` で拒否されたプロミスを扱うと、プロミスの連鎖のさらに先に影響を及ぼします。エラーを直ちに処理しなければならないため、選択の余地がない場合もあります。このような場合には、連鎖的にエラー状態を維持するために、何らかの種類のエラーを発生させる必要があります。一方で、緊急の必要性がない場合は、最後の `.catch()` ステートメントまでエラー処理を行わない方がシンプルです。 `.catch()` は、実際には、単なる `.then()` からプロミスが解決されたときのためのコールバック関数のスロットを抜いたものです。
+
+```js
+myPromise
+.then(handleResolvedA)
+.then(handleResolvedB)
+.then(handleResolvedC)
+.catch(handleRejectedAny);
+```
+
+{{JSxRef("Functions/Arrow_functions", "アロー関数式", "", 1)}}をコールバック関数に使用すると、プロミス連鎖の実装は次のようになるでしょう。
+
+```js
+promise1
+.then(value => { return value + ' and bar'; })
+.then(value => { return value + ' and bar again'; })
+.then(value => { return value + ' and again'; })
+.then(value => { return value + ' and again'; })
+.then(value => { console.log(value) })
+.catch(err => { console.log(err) });
+```
+
+プロミスの終了条件は、その連鎖内の次のプロミスの「決定」状態を決定します。解決」状態はプロミスの完了が成功したことを示し、「拒否」状態は成功しなかったことを示します。連鎖内で解決されたそれぞれのプロミスの返値は、次の `.then()` に渡され、拒否された理由は連鎖内の次の拒否ハンドラー関数に渡されます。
+
+連鎖のプロミスは、ロシア人形のように入れ子になっていますが、スタックの一番上のように取り出されます。連鎖の最初のプロミスは最も深くネストされており、最初に取り出されます。
+
+```plain
+(promise D, (promise C, (promise B, (promise A) ) ) )
+```
+
+`nextValue` がプロミスである場合、その効果は動的な置換です。 `return` によってプロミスが取り出されますが、 `nextValue` のプロミスはその場所に押し込まれます。上に示した入れ子では、"promise B" に関連付けられた `.then()` が "promise X" の `nextValue` を返すとします。 結果としての入れ子は以下のようになります。
+
+```plain
+(promise D, (promise C, (promise X) ) )
+```
+
+プロミスは複数の入れ子に参加することができます。以下のコードでは、 `promiseA` が「決定」状態に移行すると、 `.then()` の両方のインスタンスが呼び出されます。
+
+```js
+const promiseA = new Promise(myExecutorFunc);
+const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
+const promiseC = promiseA.then(handleFulfilled2, handleRejected2);
+```
+
+既に「決定」状態のプロミスにアクションを割り当てることができます。その場合、アクションは (適切であれば) 最初の非同期の機会に実行されます。プロミスは非同期であることが保証されていることに注意してください。したがって、既に「解決」状態のプロミスに対するアクションは、スタックがクリアされ、クロックティックが経過した後にのみ実行されます。この効果は `setTimeout(action,10)` とよく似ています
+
+```js
+const promiseA = new Promise( (resolutionFunc,rejectionFunc) => {
+ resolutionFunc(777);
+});
+// この時点で、 "promiseA" はすでに解決されています。
+promiseA.then( (val) => console.log("asynchronous logging has val:",val) );
+console.log("immediate logging");
+
+// 以下の順序で出力が行われます。
+// immediate logging
+// asynchronous logging has val: 777
+```
+
+### 現行の設定オブジェクトの追跡
+
+設定オブジェクト (settings object) とは、 JavaScript コードの実行時に追加情報を提供する[環境](https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object)のことです。これには、領域やモジュールマップのほか、オリジンなどの HTML 固有の情報も含まれます。現行の設定オブジェクトが追跡されるのは、特定のユーザーコードに対してどの設定オブジェクトを使用すべきかをブラウザーが確実に把握するためです。
+
+これをより良く理解するために、領域がどのように問題になるかを詳しく見てみましょう。領域 (**realm**) とは、大まかに言うとグローバルオブジェクトのことです。領域の特徴は、JavaScript のコードを実行するために必要な情報をすべて保持していることです。これには [`Array`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Array) や [`Error`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Error) などのオブジェクトが含まれます。それぞれの設定オブジェクトはこれらの「コピー」を持っており、共有されていません。そのため、プロミスとの関係で予期しない動作をすることがあります。これを回避するために、**現行の設定オブジェクト** (incumbent settings object) と呼ばれるものを追跡します。これは、ある関数呼び出しを担当するユーザーコードのコンテキストに固有の情報を表します。
+
+これをもう少し詳しく説明するために、文書に埋め込まれた [`
{{jsxref("Promise")}} は非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。多くの人々は既存の用意された Promise を使うことになるため、このガイドでは、Promise の作成方法の前に、関数が返す Promise の使い方から説明します。
- -本質的に、Promise はコールバックを関数に渡すかわりに、関数が返したオブジェクトに対してコールバックを登録するようにする、というものです。
- -例えば createAudioFileAsync() という非同期に音声ファイルを生成する関数を考えましょう。この関数はコンフィグオブジェクトと 2 つのコールバック関数を受け取り、片方のコールバックは音声ファイルが無事作成されたときに呼び出され、もう一つはエラーが発生したときに呼び出されます。
以下のコードは createAudioFileAsync() を使用したものです。
function successCallback(result) {
- console.log("Audio file ready at URL: " + result);
-}
-
-function failureCallback(error) {
- console.log("Error generating audio file: " + error);
-}
-
-createAudioFileAsync(audioSettings, successCallback, failureCallback);
-
-最近では関数は Promise を返し、代わりにその Promise にコールバックを登録することができます。
- -もし createAudioFileAsync() が Promise を返すように書き換えられたとすれば、以下のようにシンプルに使用することができます。
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);- -
これは以下のコードの短縮形です。
- -const promise = createAudioFileAsync(audioSettings); -promise.then(successCallback, failureCallback);- -
これを非同期関数呼び出し(asynchronnous function call)と呼びます。この記述方法にはいくつか利点があるので、順に説明します。
- -旧来のコールバック渡しとは異なり、Promise では以下が保証されています。
- -then() により登録されたコールバックでも、上記のように呼び出される。then() を何回も呼び出して複数のコールバックを追加してもよく、それぞれのコールバックは追加順に独立して実行される。とはいえ、最もすぐわかる Promise の利点は Promise チェーンでしょう。
- -一般的なニーズとしては、複数の非同期処理を順番に実行し、前の処理が完了してからその結果を次の処理で使うというものがあります。これは Promise チェーンを作成することで行えます。
- -さあ魔法の時間です。then() 関数は元の Promise とは別の新しい Promise を返します。
const promise = doSomething(); -const promise2 = promise.then(successCallback, failureCallback); -- -
もしくは、以下のように書いても構いません。
- -const promise2 = doSomething().then(successCallback, failureCallback); -- -
2 つ目の Promise は doSomething() の完了を表すだけではなく、渡した successCallback もしくは failureCallback の完了も表し、これらのコールバックは Promise を返すまた別の非同期関数であっても構いません。その場合、promise2 に追加されたコールバックはいずれも Promise のキューにおいて、successCallback または failureCallback が返す Promise の後ろに追加されます。
基本的に、それぞれの Promise はチェーン(連鎖)上の各非同期処理の完了を表します。
- -昔は、複数の非同期処理を順番に実行するには、従来のコールバック地獄を作ることになりました。
- -doSomething(function(result) {
- doSomethingElse(result, function(newResult) {
- doThirdThing(newResult, function(finalResult) {
- console.log('Got the final result: ' + finalResult);
- }, failureCallback);
- }, failureCallback);
-}, failureCallback);
-
-
-モダンな関数を使えば、その代わりに戻り値の Promise にコールバックを付加して Promise チェーンとして記述できます。
- -doSomething()
-.then(function(result) {
- return doSomethingElse(result);
-})
-.then(function(newResult) {
- return doThirdThing(newResult);
-})
-.then(function(finalResult) {
- console.log('Got the final result: ' + finalResult);
-})
-.catch(failureCallback);
-
-
- then 関数の引数はオプション(必須ではない)です。また、catch(failureCallback) は then(null, failureCallback) の短縮形です。記述にはアロー関数を使っても構いません。
doSomething()
-.then(result => doSomethingElse(result))
-.then(newResult => doThirdThing(newResult))
-.then(finalResult => {
- console.log(`Got the final result: ${finalResult}`);
-})
-.catch(failureCallback);
-
-
-重要: コールバック関数から処理結果を返すのを忘れないでください。さもないと後続のコールバック関数からその処理結果を利用することができなくなります (アロー関数を使った () => x は () => { return x; } の短縮形です)。
失敗、つまり catch の後にチェーンするのも可能で、これはチェーン内の動作が失敗した後でも新しい動作を行うのに便利です。次の例を読んでください:
new Promise((resolve, reject) => {
- console.log('Initial');
-
- resolve();
-})
-.then(() => {
- throw new Error('Something failed');
-
- console.log('Do this');
-})
-.catch(() => {
- console.log('Do that');
-})
-.then(() => {
- console.log('Do this whatever happened before');
-});
-
-これは下記のテキストを出力します:
- -Initial -Do that -Do this whatever happened before- -
注意:Do this
のテキストは Something failed
エラーが reject を引き起こしたため出力されないことに注意してください。
以前のコールバック地獄形式の記述方法では failureCallback を 3 回書く必要がありましたが、Promise チェーンでは failureCallback は 1 回で済みます。
doSomething()
-.then(result => doSomethingElse(result))
-.then(newResult => doThirdThing(newResult))
-.then(finalResult => console.log(`Got the final result: ${finalResult}`))
-.catch(failureCallback);
-
-
-例外が発生すると、ブラウザーはチェーンをたどって .catch() ハンドラーか onRejected を探します。この振る舞いは同期的なコードの動作と非常によく類似しています。
try {
- const result = syncDoSomething();
- const newResult = syncDoSomethingElse(result);
- const finalResult = syncDoThirdThing(newResult);
- console.log(`Got the final result: ${finalResult}`);
-} catch(error) {
- failureCallback(error);
-}
-
-
-ECMAScript 2017 のシンタックスシュガー async/await を使えば、完全にそっくりのコードになります。
async function foo() {
- try {
- const result = await doSomething();
- const newResult = await doSomethingElse(result);
- const finalResult = await doThirdThing(newResult);
- console.log(`Got the final result: ${finalResult}`);
- } catch(error) {
- failureCallback(error);
- }
-}
-
-
-async/await は Promise の上に成り立っています。例えば上記の doSomething() はこれまでと同じ(Promise を返す)関数です。この書き方の詳細についてはこちらをご覧ください。
Promise は例外やプログラミングエラーを含むすべてのエラーをとらえることで、コールバック地獄の根本的な問題を解決します。これは非同期処理を合成するのに不可欠です。
- -Promise が失敗するたびに、グローバルスコープ(通常 {{domxref("window")}} オブジェクトか、Web Worker 内ならば Worker か Worker ベースのインターフェイスをもつオブジェクト)に以下の 2 つのイベントのどちらかが送られます:
rejectionhandledreject 関数などによって処理されたあとに送られる。unhandledrejectionいずれの場合でも、イベントオブジェクト( PromiseRejectionEvent 型)は失敗した Promise を表す promise プロパティと、その Promise が失敗した理由を表す reason プロパティを持ちます。
これらのイベントを使えば、Promise のエラーハンドラーのフォールバックを指定することができ、また Promise を管理する際の問題をデバッグするのにも役立ちます。これらのイベントのハンドラーはコンテキストごとにグローバルであり、どこから発生したかに関わらず、すべてのエラーは同じイベントハンドラーによって処理されます。
- -特に便利なケースとして、{{Glossary("Node.js")}} 用のコードを書いているときにプロジェクト内のモジュールで Promise が失敗しハンドルされないことがよくあります。これらは Node.js の実行環境によりコンソールに出力されます。これらの失敗を分析したりハンドラーを設定したいとき、あるいは単にコンソールがこれらで埋め尽くされないようにしたいとき、以下のように unhandledrejection イベントのハンドラーを追加することができます。
window.addEventListener("unhandledrejection", event => {
- /* ここで該当の Promise を event.promise で、失敗の理由を
- event.reason で取得して調べることができます */
-
- event.preventDefault();
-}, false);
-
-イベントの preventDefault() メソッドを呼び出すことによって、失敗した Promise がハンドルされないときの JavaScript の実行環境のデフォルトの動作を防ぐことができます。特に Node.js がそうですが、通常はデフォルトの動作ではエラーがコンソールに出力されます。
当然ながら理想的には、これらのイベントを捨てる前に失敗した Promise を調べて、いずれもコードのバグによるものではないことを確かめるべきです。
- -{{jsxref("Promise")}} はコンストラクタを使って 1 から作ることもできます。これは古い API をラップする場合にのみ必要となるはずです。
- -理想的には、すべての非同期関数は Promise を返すはずですが、残念ながら API の中にはいまだに古いやり方で成功/失敗用のコールバックを渡しているものがあります。典型的な例としては setTimeout() 関数があります。
setTimeout(() => saySomething("10 seconds passed"), 10*1000);
-
-
-古い形式のコールバックと Promise の混在は問題を引き起こします。というのは、saySomething() が失敗したりプログラミングエラーを含んでいた場合にそのエラーをとらえられないからです。setTimeout にその責任があります。
幸いにも setTimeout を Promise の中にラップすることができます。ベストプラクティスは、問題のある関数を可能な限り低いレベルでラップした上で、二度と直接呼ばないようにするというものです。
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
-
-wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback);
-
-
-基本的に、Promise のコンストラクタには、手動で Promise を resolve もしくは reject できるようにする実行関数を渡します。setTimeout() は失敗することはないので、reject は省略しました。
{{jsxref("Promise.resolve()")}} と {{jsxref("Promise.reject()")}} はそれぞれ既に resolve もしくは reject された Promise を手動で作成するショートカットで、たまに役立つことがあります。
- -{{jsxref("Promise.all()")}} と {{jsxref("Promise.race()")}} は同時並行で実行中の非同期処理を合成するためのツールです。
- -以下のように複数の処理を並行に開始し、すべてが終了するのを待つことができます。
- -Promise.all([func1(), func2(), func3()])
-.then(([result1, result2, result3]) => { /* result1, result2, result3 が使える */ });
-
-以下のように工夫すれば、逐次実行をする直列的な合成も記述することができます。
- -[func1, func2].reduce((p, f) => p.then(f), Promise.resolve())
-.then(result3 => { /* result3 が使える */ });
-
-基本的に、これは非同期関数の配列を Promise.resolve().then(func1).then(func2).then(func3); と同等の Promise チェーンへと reduce しています。
このような処理は以下のように、関数型プログラミングでよくある再利用可能な合成関数にすることがすることができます。
- -const applyAsync = (acc,val) => acc.then(val); -const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));- -
composeAsync 関数は任意の個数の関数を引数として受け取って、1本のパイプラインとして合成された関数を返します。この関数に渡された初期値は合成された関数を通過していきます。
const transformData = composeAsync(func1, func2, func3); -const result3 = transformData(data);- -
ECMAScript 2017 では直列的な合成は async/await でもっと単純に書くことができます。
- -let result;
-for (const f of [func1, func2, func3]) {
- result = await f(result);
-}
-/* 最終的な結果(result3)が使える */
-
-想定外の事態とならないよう、たとえすでに resolve された Promise であっても、then() に渡される関数が同期的に呼ばれることはありません。
Promise.resolve().then(() => console.log(2)); -console.log(1); // 1, 2 -- -
渡された関数は、すぐに実行されるのではなくマイクロタスクのキューに入れられます。現在のイベントループの終わりにこのキューは空になったときに、この関数が実行されます(つまりかなり早い段階です)。
- -const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); - -wait().then(() => console.log(4)); -Promise.resolve().then(() => console.log(2)).then(() => console.log(3)); -console.log(1); // 1, 2, 3, 4- -
単純な Promise チェーンならば、ネストは不用意な合成の結果生まれるものなので、ネストせずに平らにしておくのがベストです。よくある間違いを参照してください。
- -ネストとは catch ステートメントのスコープを制限するための制御構造です。正確には、ネストされた catch はそのスコープ内の失敗しかキャッチせず、Promise チェーン上でスコープ外のエラーには反応しません。正しく使えばより正確にエラーからの回復ができるようになります。
doSomethingCritical()
-.then(result => doSomethingOptional(result)
- .then(optionalResult => doSomethingExtraNice(optionalResult))
- .catch(e => {})) // オプションの処理が失敗すれば無視して進める
-.then(() => moreCriticalStuff())
-.catch(e => console.log("Critical failure: " + e.message));
-
-インデントではなく外側の括弧 () によってオプションの処理がネストされていることに注意してください。
内側の catch ステートメントは doSomethingOptional() と doSomethingExtraNice() からの失敗だけをキャッチし、キャッチしたあと moreCriticalStuff() へと処理が続きます。重要なのは、もし doSomethingCritical() が失敗したらそのエラーは最後の catch によってだけキャッチされるということです。
Promise チェーンを合成するときは以下のようなよくある間違いに気をつける必要があります。以下の例にいくつかの間違いが含まれています。
- -// 悪い例。間違いを 3 つ見つけてください。
-
-doSomething().then(function(result) {
- doSomethingElse(result) // 内側のチェーンから Promise を返していない + 不必要なネスト
- .then(newResult => doThirdThing(newResult));
-}).then(() => doFourthThing());
-// チェーンの最後を catch で終わらせていない
-
-
-最初の間違いは適切にチェーンを構成できていないことです。これは、新しい Promise を作成したがそれを返すのを忘れているときに起きます。結果としてチェーンは壊れ、2 つのチェーンが独立に実行されることになります。これはつまり doFourthThing() は doSomethingElse() や doThirdThing() の終了を待たないことになり、おそらく意図せず並行して実行されることになります。別々のチェーンでは別々のエラーハンドリングが行われるため、キャッチされないエラーが発生することになります。
2 つ目の間違いは不必要にネストしていることであり、1 つめの間違いを可能にしているものでもあります。ネストするということは内側のエラーハンドラーが制限されるということであり、もしこれが意図していないものであれば、エラーがキャッチされないということが起こりえます。これの変化形で Promise コンストラクターアンチパターンというものがあり、ネストに加えて、Promise を既に使用しているコードを不必要な Promise コンストラクターでラップするというものです。
- -3 つ目の間違いはチェーンを catch で終わらせていないことです。ほとんどのブラウザーではそのようなチェーンは Promise の失敗がキャッチされないことになります。
よい経験則としては、Promise チェーンは常に return するか catch で終わらせ、新しい Promise を得るたびにすぐに return してチェーンを平らにすることです。
doSomething()
-.then(function(result) {
- return doSomethingElse(result);
-})
-.then(newResult => doThirdThing(newResult))
-.then(() => doFourthThing())
-.catch(error => console.log(error));
-
-
-() => x は () => { return x; } の短縮形であることに注意してください。
これで適切なエラーハンドリングがされた 1本のチェーンができました。
- -async/await を使えば、すべてではないにしてもほとんどの問題は解決しますが、このシンタックスで最もよくある間違いが await キーワードを忘れることであるという欠点があります。
(イベントとコールバックのような) Promise とタスクが予知できない順序で発火するような状況に陥る場合、Promise が条件付きで作成されて Promise の状態をチェックしたり帳尻合わせしたりするマイクロタスクを利用できることがあります。
- -マイクロタスクでこの問題を解決できると考えたなら、microtask guide を見て、関数をマイクロタスクでキューに入れる queueMicrotask() の使い方を学んでください。
async/await{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}
diff --git a/files/ja/web/javascript/guide/using_promises/index.md b/files/ja/web/javascript/guide/using_promises/index.md new file mode 100644 index 0000000000..35b791ab66 --- /dev/null +++ b/files/ja/web/javascript/guide/using_promises/index.md @@ -0,0 +1,410 @@ +--- +title: プロミスの使用 +slug: Web/JavaScript/Guide/Using_promises +tags: + - Guide + - Intermediate + - JavaScript + - Promise + - Promises + - asynchronous + - l10n:priority +translation_of: Web/JavaScript/Guide/Using_promises +--- +{{jsSidebar("JavaScript Guide")}}{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}} + +プロミス ({{jsxref("Promise")}}) は、非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。多くの人々は既存の用意されたプロミスを使うことになるため、このガイドでは、プロミスの作成方法の前に、関数が返すプロミスの使い方から説明します。 + +基本的に、プロミスはコールバックを関数に渡すかわりに、関数が返したオブジェクトに対してコールバックを登録するようにするというものです。 + +例えば、`createAudioFileAsync()` という非同期に音声ファイルを生成する関数を考えてみましょう。この関数は構成オブジェクトと 2 つのコールバック関数を受け取り、片方のコールバックは音声ファイルが無事作成されたときに呼び出され、もう一つはエラーが発生したときに呼び出されるとします。 + +以下のコードは `createAudioFileAsync()` を使用したものです。 + +```js +function successCallback(result) { + console.log("Audio file ready at URL: " + result); +} + +function failureCallback(error) { + console.error("Error generating audio file: " + error); +} + +createAudioFileAsync(audioSettings, successCallback, failureCallback); +``` + +`createAudioFileAsync()` をプロミスを返すように書き換えると、コールバックを次のように割り当てることができます。 + +```js +createAudioFileAsync(audioSettings).then(successCallback, failureCallback); +``` + +この記述方法にはいくつか利点があるので、順に説明します。 + +## 保証 + +旧来のコールバック*渡し*とは異なり、プロミスでは以下のことが保証されています。 + +- [`then()`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) によって追加されたコールバックは、現在の JavaScript イベントループの[現在の処理の完了](/ja/docs/Web/JavaScript/EventLoop#run-to-completion)より前には決して呼び出されません。 +- これらのコールバックは、プロミスが表す非同期操作が成功または失敗した*後*に追加したとしても呼び出されます。 +- [`then()`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) を複数回呼び出すことで、複数のコールバックを追加することができます。それぞれのコールバックは追加した順番に実行されます。 + +プロミスを使用する最大の利点の一つは**連鎖**でしょう。 + +## 連鎖 + +よくあるニーズはとして、 2 つ以上の非同期操作を連続して実行し、前の操作が成功したときに、前のステップの結果を使って後続の各操作を開始するというものがあります。プロミス連鎖を作成することで、これを実現することができます。 + +さあ魔法の時間です。`then()` 関数は元のプロミスとは別の**新しいプロミス**を返します。 + +```js +const promise = doSomething(); +const promise2 = promise.then(successCallback, failureCallback); +``` + +もしくは、以下のように書いても構いません。 + +```js +const promise2 = doSomething().then(successCallback, failureCallback); +``` + +2 つ目のプロミス (`promise2`) は `doSomething()` の完了を表すだけではなく、渡した `successCallback` もしくは `failureCallback` の完了も表し、これらのコールバックはプロミスを返すまた別の非同期関数であっても構いません。その場合、`promise2` に追加されたコールバックはいずれもプロミスのキューにおいて、`successCallback` または `failureCallback` が返すプロミスの後ろに追加されます。 + +基本的に、それぞれのプロミスは連鎖上にある個々の非同期の段階の完了を表します。 + +昔は、複数の非同期処理を順番に実行するには、従来の恐ろしいコールバック地獄を作ることになりました。 + +```js +doSomething(function(result) { + doSomethingElse(result, function(newResult) { + doThirdThing(newResult, function(finalResult) { + console.log('Got the final result: ' + finalResult); + }, failureCallback); + }, failureCallback); +}, failureCallback); +``` + +現在の機能を使えば、その代わりに返却されたプロミスににコールバックを付加してプロミス連鎖として記述できます。 + +```js +doSomething() +.then(function(result) { + return doSomethingElse(result); +}) +.then(function(newResult) { + return doThirdThing(newResult); +}) +.then(function(finalResult) { + console.log('Got the final result: ' + finalResult); +}) +.catch(failureCallback); +``` + +`then` 関数の引数は任意であり、また、 `catch(failureCallback)` は `then(null, failureCallback)` の短縮形です。記述には[アロー関数](/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions)を使っても構いません。 + +```js +doSomething() +.then(result => doSomethingElse(result)) +.then(newResult => doThirdThing(newResult)) +.then(finalResult => { + console.log(`Got the final result: ${finalResult}`); +}) +.catch(failureCallback); +``` + +**重要:** コールバック関数から処理結果を返すのを忘れないでください。さもないと後続のコールバック関数からその処理結果を利用することができなくなります (アロー関数を使った `() => x` は `() => { return x; }` の短縮形です)。 + +### catch の後の連鎖 + +失敗、つまり `catch` の後に連鎖することも可能で、これは連鎖内の動作が失敗した後でも新しい動作を行うのに便利です。次の例を読んでください。 + +```js +new Promise((resolve, reject) => { + console.log('Initial'); + + resolve(); +}) +.then(() => { + throw new Error('Something failed'); + + console.log('Do this'); +}) +.catch(() => { + console.error('Do that'); +}) +.then(() => { + console.log('Do this, no matter what happened before'); +}); +``` + +これは下記のテキストを出力します。 + +```plain +Initial +Do that +Do this, no matter what happened before +``` + +**注意:** "Do this" のテキストは "Something failed" エラーが拒否をを引き起こしたため出力されないことに注意してください。 + +## エラーの伝播 + +以前のコールバック地獄形式の記述方法では `failureCallback` を 3 回書く必要がありましたが、プロミス連鎖では 1 回で済みます。 + +```js +doSomething() +.then(result => doSomethingElse(result)) +.then(newResult => doThirdThing(newResult)) +.then(finalResult => console.log(`Got the final result: ${finalResult}`)) +.catch(failureCallback); +``` + +例外が発生すると、ブラウザーは連鎖をたどって `.catch()` ハンドラーか `onRejected` を探します。この動作は同期的なコードの動作ととてもよく似ています。 + +```js +try { + const result = syncDoSomething(); + const newResult = syncDoSomethingElse(result); + const finalResult = syncDoThirdThing(newResult); + console.log(`Got the final result: ${finalResult}`); +} catch(error) { + failureCallback(error); +} +``` + +ECMAScript 2017 の糖衣構文 [`async`/`await`](/ja/docs/Web/JavaScript/Reference/Statements/async_function) を使えば、非同期コードとそっくりになります。 + +```js +async function foo() { + try { + const result = await doSomething(); + const newResult = await doSomethingElse(result); + const finalResult = await doThirdThing(newResult); + console.log(`Got the final result: ${finalResult}`); + } catch(error) { + failureCallback(error); + } +} +``` + +async/await はプロミスの上に成り立っています。例えば上記の `doSomething()` は以前と同じ関数です。の書き方の詳細については[こちら](https://developers.google.com/web/fundamentals/getting-started/primers/async-functions)をご覧ください。 + +プロミスは例外やプログラミングエラーを含むすべてのエラーをとらえることで、コールバック地獄の根本的な問題を解決します。これは非同期処理を合成するのに不可欠です。 + +## プロミスの拒否イベント + +プロミスが拒否されるたびに、グローバルスコープ (通常は {{domxref("window")}} オブジェクトか、ウェブワーカー内ならば [`Worker`](/ja/docs/Web/API/Worker "Worker は Web Workers API のインターフェイスで、作成主に対してメッセージを送ることができる、スクリプトを通じて生成されたバックグラウンドタスクを表します。") かワーカーベースのインターフェイスをもつオブジェクト) に以下の 2 つのイベントのどちらかが送られます。 + +- [`rejectionhandled`](/ja/docs/Web/API/Window/rejectionhandled_event "rejectionhandled イベントは、 JavaScript の Promise が拒否されたものの、その後でそのプロミスの拒否が処理された場合にスクリプトのグローバルスコープ (ふつうは window だが Worker の場合もある) に送られます。") + - : プロミスが拒否されたとき、実行者の `reject` 関数によって拒否が処理された後に送られます。 +- [`unhandledrejection`](/ja/docs/Web/API/Window/unhandledrejection_event "unhandledrejection イベントは、 JavaScript の拒否ハンドラーを持たない Promise が拒否されたときにスクリプトのグローバルスコープに送られます。 通常、これは window ですが、 Worker であることもあります。") + - : プロミスが拒否されたものの、拒否ハンドラーが利用できない場合に送られます。 + +いずれの場合でも、この ([`PromiseRejectionEvent`](/ja/docs/Web/API/PromiseRejectionEvent "PromiseRejectionEvent インターフェイスは、 JavaScript の Promise が拒否されたときにグローバルスクリプトコンテキストに送信されるイベントを表します。") 型のイベントは、拒否されたプロミスを示す [`promise`](/ja/docs/Web/API/PromiseRejectionEvent/promise "PromiseRejectionEvent インターフェイスの読み取り専用プロパティである promise は、拒否された JavaScript の Promise を表します。そのプロミスが拒絶された理由は、イベントの PromiseRejectionEvent.reason プロパティを検査するとわかります。") プロパティと、そのプロミスが失敗した理由を表す [`reason`](/ja/docs/Web/API/PromiseRejectionEvent/reason "PromiseRejectionEventの reason 読み取り専用プロパティは、Promise.reject() に渡される理由を提供する任意の JavaScript 値、または Object です。理論的にはプロミスが拒否された理由についての情報を提供します。") プロパティを持ちます。 + +これらのイベントを使えば、プロミスのエラー処理のフォールバックを指定することができ、またプロミスを管理する際の問題をデバッグするのにも役立ちます。これらのハンドラーはコンテキストごとにグローバルであり、発生元に関わらず、すべてのエラーが同じイベントハンドラーによって処理されます。 + +特に便利なケースとして、 [Node.js](/ja/docs/Glossary/Node.js) 用のコードを書いているときに、プロジェクト内のモジュールでプロミスが拒否され処理されないことがよくあります。これらは Node.js の実行環境ではコンソールに出力されます。次のようにして Node.js に `unhandledRejection` イベントのハンドラーを追加することで、これを捕えて分析したり、自分のコードで処理したり—または、出力が埋め尽くされないようにしたいだけの場合も—することができます。 + +```js +process.on("unhandledRejection", (reason, promise) => { + /* ここにコードを追加することで、 "promise" および "reason" の値を + * 検査することができます。 */ +}); +``` + +Node.js では、エラーがコンソールに記録されること (そうしないと発生する既定のアクション) を防ぐには、 `process.on()` リスナーを追加するだけで十分です。ブラウザーの実行環境の [`preventDefault()`](/ja/docs/Web/API/Event/preventDefault) のようなことを行う必要はありません。 + +しかし、 `process.on` リスナーを追加しても、その中に拒否されたプロミスを処理するコードがなければ、プロミスは床に落ちて暗黙に無視されてしまいます。そのため、できればリスナー内にコードを追加して、拒否されたプロミスをそれぞれ検証し、実際のコードのバグが原因ではないことを確認してください。 + +## 古いコールバック API をラップする Promise の作成 + +{{jsxref("Promise")}} はコンストラクターを使って 1 から生成すこともできます。これが必要になるのは古い API をラップする場合のみでしょう。 + +理想的には、すべての非同期関数はプロミスを返すはずですが、残念ながら API の中にはいまだに古いやり方で成功/失敗用のコールバックを渡しているものがあります。顕著な例としては [`setTimeout()`](/ja/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) 関数があります。 + +```js +setTimeout(() => saySomething("10 seconds passed"), 10*1000); +``` + +古い様式であるコールバックとプロミスの混在は問題を引き起こします。というのは、`saySomething()` が失敗したりプログラミングエラーを含んでいた場合に、そのエラーをとらえられないからです。`setTimeout` にその責任があります。 + +幸いにも `setTimeout` をプロミスの中にラップすることができます。良いやり方は、問題のある関数をできる限り低い水準でラップした上で、直接呼び出さないようにすることです。 + +```js +const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + +wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback); +``` + +基本的に、プロミスのコンストラクターには、手動でプロミスを解決または拒否できるようにする実行関数を渡します。実際には `setTimeout()` は失敗することがないので、この場合は拒否を省略しました。 + +## 合成 + +{{jsxref("Promise.resolve()")}} と {{jsxref("Promise.reject()")}} は、それぞれ既に解決または拒否されたプロミスを手動で生成するショートカットです。たまに役立つことがあります。 + +{{jsxref("Promise.all()")}} と {{jsxref("Promise.race()")}} は並行して実行されている非同期処理を合成するためのツールです。 + +以下のように複数の処理を並行に開始し、すべてが終了するのを待つことができます。 + +```js +Promise.all([func1(), func2(), func3()]) +.then(([result1, result2, result3]) => { /* result1, result2, result3 を使用 */ }); +``` + +以下のように工夫すれば、逐次実行をする直列的な合成も記述することができます。 + +```js +[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve()) +.then(result3 => { /* result3 を使用 */ }); +``` + +基本的に、これは非同期関数の配列を `Promise.resolve().then(func1).then(func2).then(func3);` と同等のプロミス連鎖へとまとめます。 + +これは、関数型プログラミングでよく見られる、再利用可能な合成関数にすることができます。 + +```js +const applyAsync = (acc,val) => acc.then(val); +const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x)); +``` + +`composeAsync()` 関数は、任意の数の関数を引数として受け取り、合成パイプラインに渡される初期値を受け取る新しい関数を返します。 + +```js +const transformData = composeAsync(func1, func2, func3); +const result3 = transformData(data); +``` + +ECMAScript 2017 では、直列的な合成は async/await でもっと単純に書くことができます。 + +```js +let result; +for (const f of [func1, func2, func3]) { + result = await f(result); +} +/* 最終的な結果 (すなわち result3) を使用 */ +``` + +## タイミング + +想定外の事態とならないよう、たとえすでに解決されたプロミスであっても、[`then()`](/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) に渡された関数が同期的に呼び出されることはありません。 + +```js +Promise.resolve().then(() => console.log(2)); +console.log(1); // 1, 2 +``` + +渡された関数はすぐに実行されるのではなく、マイクロタスクのキューに入れられます。後で (生成した関数が終了し、 JavaScript の実行スタックが空になってから)、イベントループに制御が戻される直前、つまりかなり早い段階で実行されます。 + +```js +const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + +wait(0).then(() => console.log(4)); +Promise.resolve().then(() => console.log(2)).then(() => console.log(3)); +console.log(1); // 1, 2, 3, 4 +``` + +#### タスクキューとマイクロタスク + +プロミスのコールバックは[マイクロタスク](/ja/docs/Web/API/HTML_DOM_API/Microtask_guide)として処理され、 [`setTimeout()`](/ja/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) コールバックはタスクキューとして扱われます。 + +```js +const promise = new Promise(function(resolve, reject) { + console.log("Promise callback"); + resolve(); +}).then(function(result) { + console.log("Promise callback (.then)"); +}); + +setTimeout(function() { + console.log("event-loop cycle: Promise (fulfilled)", promise) +}, 0); + +console.log("Promise (pending)", promise); +``` + +上記のコードの出力は次の通りです。 + +```plain +Promise callback +Promise (pending) Promise {unhandledrejection および rejectionhandled をデフォルトで有効にしました ({{bug(1362272)}})。これらの動作について詳しくは {{SectionOnPage("/docs/Web/JavaScript/Guide/Using_promises", "Promise rejection events")}} をご覧ください。unhandledrejection および rejectionhandled をデフォルトで有効にしました ({{bug(1362272)}})。これらの動作について詳しくは {{SectionOnPage("/docs/Web/JavaScript/Guide/Using_promises", "Promise rejection events")}} をご覧ください。requestStorageAccess() は {{domxref("Document")}} インターフェイスのメソッドで、ファーストパーティのストレージへのアクセスが許可されたら解決し、アクセスが拒否されたら拒絶される {{jsxref("Promise")}} を返します。
requestStorageAccess() は {{domxref("Document")}} インターフェイスのメソッドで、ファーストパーティのストレージへのアクセスが許可されたら解決し、アクセスが拒否されたら拒否される {{jsxref("Promise")}} を返します。
allow-storage-access-by-user-activation がなければ、拒絶。allow-storage-access-by-user-activation がなければ、拒否。requestStorageAccess() の呼び出しのためにその事実を保存。ファーストパーティのストレージへのアクセスが許可されたら undefined で満たされ、アクセスが拒否されたら拒絶される {{jsxref("Promise")}} です。
ファーストパーティのストレージへのアクセスが許可されたら undefined で満たされ、アクセスが拒否されたら拒否される {{jsxref("Promise")}} です。
Promise が解決されると、 Promise が満たされたか拒否されたかに関わらず、ユーザージェスチャーが処理されているかのように解決ハンドラーが実行されます。
diff --git a/files/ja/web/api/mediadevices/getusermedia/index.html b/files/ja/web/api/mediadevices/getusermedia/index.html index 2ae1912292..53f62988fc 100644 --- a/files/ja/web/api/mediadevices/getusermedia/index.html +++ b/files/ja/web/api/mediadevices/getusermedia/index.html @@ -24,7 +24,7 @@ translation_of: Web/API/MediaDevices/getUserMediaこれは {{domxref("MediaStream")}} オブジェクトに解決する {{jsxref("Promise")}} を返します。ユーザーが拒否した場合や、一致するメディアが利用できない場合、 Promise はそれぞれ NotAllowedError または NotFoundError で拒否されます。
メモ: ユーザーが選択する必要が全くなく、リクエストを単純に無視できる場合、返却された Promise が解決または拒絶のどちらにもならない可能性があります。
+メモ: ユーザーが選択する必要が全くなく、リクエストを単純に無視できる場合、返却された Promise が解決または拒否のどちらにもならない可能性があります。
一般的に、 {{domxref("MediaDevices")}} のシングルトンオブジェクトは以下のように、 {{domxref("navigator.mediaDevices")}} を使用してアクセスします。
diff --git a/files/ja/web/api/navigator/getbattery/index.html b/files/ja/web/api/navigator/getbattery/index.html index a17409ec9a..b324dfb6eb 100644 --- a/files/ja/web/api/navigator/getbattery/index.html +++ b/files/ja/web/api/navigator/getbattery/index.html @@ -30,7 +30,7 @@ translation_of: Web/API/Navigator/getBatteryこのメソッドは真の例外を発生させません。代わりに、返された promise を拒絶し、 {{domxref("DOMException")}} の中に name を次のうちの一つに設定します。
このメソッドは真の例外を発生させません。代わりに、返されたプロミスが拒否され、 {{domxref("DOMException")}} の中に name を次のうちの一つに設定します。
SecurityErrorPromiseRejectionEvent インターフェイスは、 JavaScript の {{jsxref("Promise")}} が拒絶されたときにグローバルスクリプトコンテキストに送信されるイベントを表します。これらのイベントは、測定やデバッグ目的に使用すると特に有益です。
PromiseRejectionEvent インターフェイスは、 JavaScript の {{jsxref("Promise")}} が拒否されたときにグローバルスクリプトコンテキストに送信されるイベントを表します。これらのイベントは、測定やデバッグ目的に使用すると特に有益です。
promise は、拒絶された JavaScript の {{jsxref("Promise")}} を表します。promise が拒絶された理由は、イベントの {{domxref("PromiseRejectionEvent.reason")}} プロパティを検査することでわかります。promise は、拒否された JavaScript の {{jsxref("Promise")}} を表します。promise が拒否された理由は、イベントの {{domxref("PromiseRejectionEvent.reason")}} プロパティを検査することでわかります。reject(拒絶) されて、処理されていない JavaScript の{{jsxref("Promise")}}。
+reject(拒否) されて、処理されていない JavaScript の{{jsxref("Promise")}}。
PromiseRejectionEvent() コンストラクタは、JavaScript の {{jsxref("Promise")}} が拒絶されたときに発火するイベントを表す {{domxref("PromiseRejectionEvent")}} を新しく生成し返します。
PromiseRejectionEvent() コンストラクタは、JavaScript の {{jsxref("Promise")}} が拒否されたときに発火するイベントを表す {{domxref("PromiseRejectionEvent")}} を新しく生成し返します。
typePromiseRejectionEvent の型名を表す文字列。これは大文字小文字を区別し、{{event("rejectionhandled", '"rejectionhandled"')}} か {{event("unhandledrejection", '"unhandledrejection"')}} のいずれかである必要がある。promisereasonrejectionhandled イベントは、 JavaScript の {{jsxref("Promise")}} が拒絶されたものの、その後で Promise の拒絶が処理された場合にスクリプトのグローバルスコープ (ふつうは {{domxref("window")}} だが {{domxref("Worker")}} の場合もある) に送られます。これは、 Promise が拒絶されたが拒絶の手助けがないときに送信される {{domxref("Window.unhandledrejection_event", "unhandledrejection")}} イベントと並行して、デバッグや一般的なアプリケーションの回復に使用できます。
rejectionhandled イベントは、 JavaScript の {{jsxref("Promise")}} が拒否されたものの、その後で Promise の拒否が処理された場合にスクリプトのグローバルスコープ (ふつうは {{domxref("window")}} だが {{domxref("Worker")}} の場合もある) に送られます。これは、 Promise が拒否されたが拒否の手助けがないときに送信される {{domxref("Window.unhandledrejection_event", "unhandledrejection")}} イベントと並行して、デバッグや一般的なアプリケーションの回復に使用できます。
| プロミスの拒絶 | +プロミスの拒否 |
- JavaScript のプロミスが拒絶されたときにグローバルスクリプトコンテキストに送信されるイベント。 +JavaScript のプロミスが拒否されたときにグローバルスクリプトコンテキストに送信されるイベント。 |
イベントは Window で発行される。 |