初めに
前回に引き続きPromiseの学習を行います。
【JavaScript】Promiseをしっかり理解したい【その1】
Promiseの書き方
前回も軽く触れましたがPromiseを使った非同期処理の書き方について見ていきましょう。
function asyncFunc(data) { // ①非同期処理を関数としてまとめる
return new Promise((resolve, reject) => { // ②Promiseをインスタンス化し戻り値に設定
setTimeout(() => {
console.log('--処理実行--');
if (data) {
resolve(data * 2); // ③処理の成功時にresolveを呼び出す
} else {
reject('未入力です'); // ④処理の失敗時にrejectを呼び出す
}
}, 3000);
});
}
asyncFunc(2) // ⑤非同期関数の呼び出し
.then((result) => { // ⑥Promiseオブジェクトでresolveされた場合thenメソッドへ処理が移る
console.log(result);
})
.catch((error) => { // ⑦Promiseオブジェクトでrejectされた場合catchメソッドへ処理が移る
console.log(error);
});
--処理実行--
4
もちろん他にも書き方はありますが、上記の例が個人的に簡潔でわかりやすいかなと思います。中身を見ていきましょう。①非同期処理をasyncFunc
のように関数としてまとめます。②その関数では、Promiseオブジェクトが戻り値として返されるように記述します。⑤まとめた関数を呼び出し、③Promise内でresolveされた場合、⑥Promiseのインスタンスメソッドであるthen
に処理が移ります。then
メソッドでは主に非同期処理が完了した後に実行したい処理を記述します。また、④Promise内でrejectされた場合、⑦同じくPromiseのインスタンスメソッドであるcatch
に処理が移ります。rejectしてあげることでエラーハンドリングをすることができます。
Promiseを使用しなかった場合
もし、上記の例でPromiseを使用しなかった場合はどうなるでしょうか。
function asyncFunc(data) {
setTimeout(() => {
console.log('--処理実行--');
if (data) {
return data * 2;
} else {
return '未入力です';
}
}, 3000);
}
const result = asyncFunc(2);
console.log(result);
undefined
--処理実行--
Promiseを使用しなかった場合、setTimeout関数内の処理が始まる前に最後の行のconsole.logが呼び出されてしまっていることがわかります。
thenメソッド
then
はPromiseのインスタンスメソッドです。
const p = new Promise(...);
p.then(onFulfilled, onRejected);
onFulfilled
Promiseオブジェクト内でresolveした時、onFulfilled
が呼ばれます。
onRejected
Promiseオブジェクト内でrejectした時、onRejected
が呼ばれます。
onFulfilled
とonRejected
はどちらもオプショナル(任意)な引数なので、設定しなくても構いません。
thenメソッドでonRejected
が扱えるということは、thenメソッドだけで成功時と失敗時の両方の処理が書けるということになります。
今までの例でエラー処理はcatchメソッドを使用していましたが以下のようにthenメソッドで一纏めにすることも可能です。
function asyncFunc(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('--処理実行--');
if (data) {
resolve(data * 2);
} else {
reject('未入力です');
}
}, 3000);
});
}
asyncFunc() // 引数なし
.then(
(result) => { // ①onFulfilled
console.log(result);
},
(error) => { // ②onRejected
console.log(error);
}
);
--処理実行--
未入力です
上記の例では、resolveされた場合は①のonFulfilled
が呼ばれ、rejectされた場合は②のonRejected
が呼ばれます。asyncFunc関数の呼び出し時に引数を渡していないので、Promiseオブジェクト内でrejectされ、実行結果からもonRejected
が呼び出されていることがわかります。
catchメソッド
catch
もthenと同様にPromiseのインスタンスメソッドです。
const p = new Promise(...);
p.catch(onRejected);
catchメソッドの場合はonRejected
しかありません。
今までの例でもエラー処理はcatchメソッドを使用してきました。個人的にthenメソッドでonFulfilled
とonRejected
の両方を書くよりも、catchメソッドを使って処理を別々に分けたほうがわかりやすいかと思っています。
Promiseの状態
Promiseオブジェクトには3つの状態
というものがあります。
Fulfilled
Promiseオブジェクト内でresolve(成功)した時。このときonFulfilled
が呼ばれます。
Rejected
Promiseオブジェクト内でreject(失敗)した時。このときonRejected
が呼ばれます。
Pending
Fulfilled
またはRejected
ではない時。Promiseオブジェクトが作成された初期状態や処理の実行中がこの状態に当てはまります。
Promiseオブジェクトの状態は、一度Pending
からFulfilled
やRejected
に移ると、そのPromiseオブジェクトの状態はそれ以降変化しません。なので、then等に書いた処理が実行されるのは一度きりになります。
また、このことからFulfilled
とRejected
のどちらかの状態であることを、「確立した、不変な」を意味するSettled
と表現することがあるそうです。
Settled
Promiseオブジェクト内でresolve(成功) または reject(失敗) した時。つまりFulfilled
かRejected
の状態である時。
Promiseを抽象化するとPending(未決)
からSettled(解決)
の状態変化である、と言えるでしょう。Promiseの状態については実装等で直接使用することはありませんが、Promiseの理解の為に頭の片隅に入れておくと良いかと思われます。
まとめ
実際に実装する分には、今回のPromiseの書き方だけ押さえておけば問題ないかと思います。ただ、根本的な理解だとまだまだ足りないことが多いと感じているので引き続き学習していきたいと思います。