<commit_before>[[how-to-write-promise]]
== Promiseの書き方

Promiseの基本的な書き方について解説します。

=== promiseオブジェクトの作成

promiseオブジェクトを作る流れは以下のようになっています。

. `new Promise(fn)` の返り値がpromiseオブジェクト
. `fn`には非同期等の何らかの処理を書く
    * 処理結果が正常なら、`resolve(結果の値)` を呼ぶ
    * 処理結果がエラーなら、`reject(Errorオブジェクト)` を呼ぶ


この流れに沿ってものを実際に書いてみましょう。

非同期処理であるXMLHttpRequest(XHR)を使いデータを取得するものをPromiseで書いていきます。

==== XHRのpromiseオブジェクトを作る

まずは、XHRをPromise使って包んだような`getURL`という関数を作ります。

[role="executable"]
[[xhr-promise.js]]
.xhr-promise.js
[source,javascript]
----
include::embed/embed-xhr-promise.js[]
// 実行例
var URL = "http://httpbin.org/status/500";
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){
    console.error(error);
});
----

この`getURL`では、
XHRでの取得結果のステータスコードが200の場合のみ `resolve` - つまり取得に成功、
それ以外はエラーであるとして `reject` しています。

`resolve(req.response)` ではレスポンスの内容を引数に入れています。
resolveの引数に入れる値には特に決まりはありませんが、コールバックと同様に次の処理へ渡したい値を入れるといいでしょう。
(この値は`then`メソッドで受け取ることが出来ます)

Node.jsをやっている人は、コールバックを書く時に `callback(error, response)` と第一引数にエラーオブジェクトを
入れることがよくあると思いますが、Promiseでは役割がresolve/rejectで分担されているので、
resolveにはresponseの値のみをいれるだけで問題ありません。


次に、`reject` の方を見て行きましょう。

XHRで`onerror`のイベントが呼ばれた場合はもちろんエラーなので`reject`を呼びます。
ここで`reject`に渡している値に注目してみてください。

エラーの場合は `reject(new Error(req.statusText));` というように、Errorオブジェクトを作成して渡している事がわかると思います。
`reject` に渡す値に制限はありませんが、一般的にErrorオブジェクト(またはErrorオブジェクトを継承したもの)を渡すことになっています。

`reject` に渡す値は、rejectする理由を書いたErrorオブジェクトとなっています。
今回は、ステータスコードが200以外であるならrejectするとしていたため、`reject`にはstatusTextを入れています。
(この値は`then`メソッドの第二引数 or `catch` メソッドで受け取ることが出来ます)

=== promiseオブジェクトに処理を書く

先ほどの作成したpromiseオブジェクトを返す関数を実際に使ってみましょう

[source,javascript]
getURL("http://example.com/"); // => promiseオブジェクトが返ってくる

<<promises-overview,Promises Overview>> でも簡単に紹介したようにpromiseオブジェクトは幾つかインスタンスメソッドを持っており、
これを使いpromiseオブジェクトの状態に応じて一度だけ呼ばれるコールバックとなる関数を登録します。

promiseオブジェクトに登録する処理は以下の2種類が主となります

* promiseオブジェクトが **resolve** された時の処理(onFulfilled)
* promiseオブジェクトが **reject** された時の処理(onRejected)

[[promise-value-flow]]
.promise value flow
image::img/promise-onFulfilled_onRejected.png[promise-resolve-flow]

まずは、`getURL`で通信が成功して値が取得出来た場合の処理を書いてみましょう。

この場合の _通信が成功した_ というのは、
resolveされたことにより_promiseオブジェクトがFulFilledの状態になった_ 時という事ですね。

**resolve**された時の処理は、 <<promise.then,`.then`>> メソッドに呼ばれたい関数を渡すことで行えます。

[source,javascript]
----
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){ // <1>
    console.log(value);
});
----
<1> 分かりやすくするため関数に `onFulfilled` という名前を付けています

<<xhr-promise.js,getURL関数>> 内で `resolve(req.response);` によってpromiseオブジェクトが解決されると、
値と共に`onFulfilled` 関数が呼ばれます。

このままでは通信エラーが起きた場合などに何も処理がされないため、
今度は、`getURL` で何らかの問題があってエラーが起きた場合の処理を書いてみましょう。

この場合の _エラーが起きた_ というのは、
rejectされたことより_promiseオブジェクトがRejectedの状態になった_時という事ですね。

**reject**された時の処理は、<<promise.then,`.then`の第二引数>> または <<promise.catch,`.catch`>> メソッドに呼ばれたい関数を渡す事で行えます。

先ほどのソースにrejectされた場合の処理を追加してみましょう。

[source,javascript]
----
var URL = "http://httpbin.org/status/500"; // <1>
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){ // <2>
    console.error(error);
});
----
<1> サーバはステータスコード500のレスポンスを返す
<2> 分かりやすくするため関数 `onRejected` という名前を付けています


`getURL`の処理中が何らかの理由で例外が起きた場合 または明示的にrejectされた場合に、
その理由(Errorオブジェクト)と共に <<promise.catch,`.catch`>> の処理が呼ばれます。

<<promise.catch,`.catch`>>は`promise.then(undefined, onRejected)`のエイリアスであるため、
同様の処理は以下のように書くことも出来ます。

[[catch-as-alias]]
[source,javascript]
----
getURL(URL).then(onFulfilled, onRejected);// <1>
----
<1> onFulfilled, onRejected それぞれは先ほどと同じ関数

基本的には、<<promise.catch,`.catch`>>を使いresolveとrejectそれぞれを別々に処理した方がよいと考えられますが、
両者の違いについては <<then-or-catch,thenとcatchの違い>> で紹介します。

==== まとめ

この章では以下のことについて簡単に紹介しました。

* `new Promise` を使いpromiseオブジェクトの作成
* <<promise.then,`.then`>> や <<promise.catch,`.catch`>> を使ったpromiseオブジェクトの処理

Promiseの基本的な書き方について学びました。
他の多くの処理はこれを発展させたり、用意された静的メソッドを利用したものになります。

ここでは、同様の事はコールバック関数を渡す形でも出来るのに対してPromiseで書くメリットについては触れていませんでした。
次の章では、Promiseのメリットであるエラーハンドリングの仕組みをコールバックベースの実装と比較しながら見て行きたいと思います。<commit_msg>refactor(Ch1): サンプルのデフォルトは成功にする<commit_after>[[how-to-write-promise]]
== Promiseの書き方

Promiseの基本的な書き方について解説します。

=== promiseオブジェクトの作成

promiseオブジェクトを作る流れは以下のようになっています。

. `new Promise(fn)` の返り値がpromiseオブジェクト
. `fn`には非同期等の何らかの処理を書く
    * 処理結果が正常なら、`resolve(結果の値)` を呼ぶ
    * 処理結果がエラーなら、`reject(Errorオブジェクト)` を呼ぶ


この流れに沿ってものを実際に書いてみましょう。

非同期処理であるXMLHttpRequest(XHR)を使いデータを取得するものをPromiseで書いていきます。

==== XHRのpromiseオブジェクトを作る

まずは、XHRをPromise使って包んだような`getURL`という関数を作ります。

[role="executable"]
[[xhr-promise.js]]
.xhr-promise.js
[source,javascript]
----
include::embed/embed-xhr-promise.js[]
// 実行例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){
    console.error(error);
});
----

この`getURL`では、
XHRでの取得結果のステータスコードが200の場合のみ `resolve` - つまり取得に成功、
それ以外はエラーであるとして `reject` しています。

`resolve(req.response)` ではレスポンスの内容を引数に入れています。
resolveの引数に入れる値には特に決まりはありませんが、コールバックと同様に次の処理へ渡したい値を入れるといいでしょう。
(この値は`then`メソッドで受け取ることが出来ます)

Node.jsをやっている人は、コールバックを書く時に `callback(error, response)` と第一引数にエラーオブジェクトを
入れることがよくあると思いますが、Promiseでは役割がresolve/rejectで分担されているので、
resolveにはresponseの値のみをいれるだけで問題ありません。


次に、`reject` の方を見て行きましょう。

XHRで`onerror`のイベントが呼ばれた場合はもちろんエラーなので`reject`を呼びます。
ここで`reject`に渡している値に注目してみてください。

エラーの場合は `reject(new Error(req.statusText));` というように、Errorオブジェクトを作成して渡している事がわかると思います。
`reject` に渡す値に制限はありませんが、一般的にErrorオブジェクト(またはErrorオブジェクトを継承したもの)を渡すことになっています。

`reject` に渡す値は、rejectする理由を書いたErrorオブジェクトとなっています。
今回は、ステータスコードが200以外であるならrejectするとしていたため、`reject`にはstatusTextを入れています。
(この値は`then`メソッドの第二引数 or `catch` メソッドで受け取ることが出来ます)

=== promiseオブジェクトに処理を書く

先ほどの作成したpromiseオブジェクトを返す関数を実際に使ってみましょう

[source,javascript]
getURL("http://example.com/"); // => promiseオブジェクトが返ってくる

<<promises-overview,Promises Overview>> でも簡単に紹介したようにpromiseオブジェクトは幾つかインスタンスメソッドを持っており、
これを使いpromiseオブジェクトの状態に応じて一度だけ呼ばれるコールバックとなる関数を登録します。

promiseオブジェクトに登録する処理は以下の2種類が主となります

* promiseオブジェクトが **resolve** された時の処理(onFulfilled)
* promiseオブジェクトが **reject** された時の処理(onRejected)

[[promise-value-flow]]
.promise value flow
image::img/promise-onFulfilled_onRejected.png[promise-resolve-flow]

まずは、`getURL`で通信が成功して値が取得出来た場合の処理を書いてみましょう。

この場合の _通信が成功した_ というのは、
resolveされたことにより_promiseオブジェクトがFulFilledの状態になった_ 時という事ですね。

**resolve**された時の処理は、 <<promise.then,`.then`>> メソッドに呼ばれたい関数を渡すことで行えます。

[source,javascript]
----
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){ // <1>
    console.log(value);
});
----
<1> 分かりやすくするため関数に `onFulfilled` という名前を付けています

<<xhr-promise.js,getURL関数>> 内で `resolve(req.response);` によってpromiseオブジェクトが解決されると、
値と共に`onFulfilled` 関数が呼ばれます。

このままでは通信エラーが起きた場合などに何も処理がされないため、
今度は、`getURL` で何らかの問題があってエラーが起きた場合の処理を書いてみましょう。

この場合の _エラーが起きた_ というのは、
rejectされたことより_promiseオブジェクトがRejectedの状態になった_時という事ですね。

**reject**された時の処理は、<<promise.then,`.then`の第二引数>> または <<promise.catch,`.catch`>> メソッドに呼ばれたい関数を渡す事で行えます。

先ほどのソースにrejectされた場合の処理を追加してみましょう。

[source,javascript]
----
var URL = "http://httpbin.org/status/500"; // <1>
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){ // <2>
    console.error(error);
});
----
<1> サーバはステータスコード500のレスポンスを返す
<2> 分かりやすくするため関数 `onRejected` という名前を付けています


`getURL`の処理中が何らかの理由で例外が起きた場合 または明示的にrejectされた場合に、
その理由(Errorオブジェクト)と共に <<promise.catch,`.catch`>> の処理が呼ばれます。

<<promise.catch,`.catch`>>は`promise.then(undefined, onRejected)`のエイリアスであるため、
同様の処理は以下のように書くことも出来ます。

[[catch-as-alias]]
[source,javascript]
----
getURL(URL).then(onFulfilled, onRejected);// <1>
----
<1> onFulfilled, onRejected それぞれは先ほどと同じ関数

基本的には、<<promise.catch,`.catch`>>を使いresolveとrejectそれぞれを別々に処理した方がよいと考えられますが、
両者の違いについては <<then-or-catch,thenとcatchの違い>> で紹介します。

==== まとめ

この章では以下のことについて簡単に紹介しました。

* `new Promise` を使ったpromiseオブジェクトの作成
* <<promise.then,`.then`>> や <<promise.catch,`.catch`>> を使ったpromiseオブジェクトの処理

Promiseの基本的な書き方について学びました。
他の多くの処理はこれを発展させたり、用意された静的メソッドを利用したものになります。

ここでは、同様の事はコールバック関数を渡す形でも出来るのに対してPromiseで書くメリットについては触れていませんでした。
次の章では、Promiseのメリットであるエラーハンドリングの仕組みをコールバックベースの実装と比較しながら見て行きたいと思います。
{'fraction_non_alphanumeric': 0.1333989662810731, 'fraction_numerical': 0.005660841742554762, 'mean_word_length': 8.923076923076923, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5511567', 'n_tokens_mistral': 5416, 'n_tokens_neox': 5064, 'n_words': 356}