【JavaScript】非同期処理をPromiseで制御する
そもそも同期処理って?
コードが上から書かれた順序に従って、実行される処理。
1つのコードの処理が完了しないと次の行のコードには進めない。
非同期処理とは
特定のコード(例えばHTTPリクエスト、データベース操作など)が背後で実行されている間にも、プログラムが次の行の行のコードの実行を続けられる処理のこと。
非同期処理
- HTTPリクエスト fetchなど
- データベース操作
- ファイル読み書き
- setTimeout関数 などなど
非同期処理の結果を扱いやすくするための便利ツール
非同期処理の実行・完了を制御できる
3つのログを表示するコード
const displayMessage = () => {
setTimeout(() => {
console.log(`disPlayMessage関数が実行`);
}, 2000);
};
console.log("関数を実行します。");
displayMessage();
console.log("関数が実行されました。");
出力結果
関数を実行します。
関数が実行されました。
displayMessage関数が実行
setTImeout関数は非同期処理なので、displayMessage関数が実行というログは、同期処理の最後のconsole.log("関数が実行されました。)による出力が終わった後に表示される。
displayMessage関数が実行というメッセージ表示の完了を待ってから、関数が実行されました。を表示させたい場合、非同期処理の結果を扱いやすくするPromise、Async/Awaitなどを用いると制御が可能になる。
Promiseとは
- JavaScriptnに標準で備わっているビルトインオブジェクト
- 非同期処理が完了し、その結果が成功か失敗かに応じて適切な処理を行うことができる
- 3種類の状態を持つ
- pending(待機):非同期処理が終わっていない状態
- fulfilled(成功):非同期処理が正常に完了した状態
- rejected(拒否):非同期処理が失敗した状態
const displayMessage = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`displayMessage関数が実行`);
resolve(); // Promiseが解決される
}, 2000);
});
};
console.log("関数を実行します。");
displayMessage().then(() => {
console.log("関数が実行されました。");
});
出力結果
関数を実行します。
displayMessage関数が実行
関数が実行されました。
return new Promise((resolve, reject) => {処理});
Promiseの引数に関数を設定し、その関数の第1引数にはresolve、第2引数にはrejectを設定する。(慣例的にresolveとrejectと書くらしい)
重要なのは第1引数が成功時の処理、第2引数が失敗時の処理を担う。第2引数は省略可能。
resolve()が実行
非同期処理が正常に完了したことを表し、Promiseの状態はfulfilledに変わる。
reject()が実行
非同期処理が失敗したことを表し、Promiseの状態はrejectedに変わる。
もっと細かく(自分なりの理解)
PromiseはJavaScriptに備わっているビルトインオブジェクト。(クラス、設計図のような理解)
new Promise(...)でPromiseをインスタンス化して実体をつくる。
インスタンス化されたPromiseオブジェクトはthenやcatchメソッドを持っており、Promiseオブジェクトの状態によって、これらを使い分けできるようになる!
作成時点ではPromiseの状態はpendingのまま。
return new Promise(...)で作成したインスタンスを呼び出し元の関数displayMesage()に返している。
そうすると、呼び出し元の関数displayMessage()が状態によってthenまたはcatchメソッドを実行する。
関数の中でresolve()が実行されるとPromiseオブジェクトはfulfilled状態に変化し、thenの処理が実行される。
reject()が実行されるか、エラーが発生すると(throw new Error()を実行)、Promiseオブジェクトは``rejected状態に変化し。catchの処理が実行される。
追記(静的メソッドを使う場合)
Promiseは静的メソッドのresolve、rejectを持っているため、インスタンス化しなくてもPromise.resolve(value)、Promise.reject(value)与えられたvalueで解決することができる!
使い分け
new Promise
以下のような非同期処理を行い、非同期の処理が完了した後にresolveやrejectを呼び出したいとき
setTimeoutfetch- データベース操作
- ファイル読み書き,など
Promiseの静的メソッド
-
非同期処理を行わないときで、決まった値で
resolveやrejectして、チェーンメソッドを使いたい場合 -
Promise.resolve:特定の値ですぐにPromiseをfulfillしたいとき -
Promise.reject:発生したエラーですぐにPromiseをrejectしたいとき
コード
new Promiseで非同期処理を制御する場合
const func = () => {
return new Promise((resolve, reject) => {
// 非同期処理を実行
setTimeout(() => {
const result = Math.random();
if (result > 0.5) {
resolve('成功');
} else {
reject(new Error('失敗'));
}
}, 1000);
});
};
func()
.then((value) => {
console.log(value);
})
.catch((error) => {
console.error(error);
});
// fetchするとき
fetch('https://example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('ネットワーク応答が正常ではありません');
}
return response.json();
})
.then(data => {
console.log('取得したデータ:', data);
})
.catch(error => {
console.error('エラーが発生しました:', error);
});
fetchはsetTimeoutと違ってPromiseを返す関数なので、new Promiseでラップしなくてもよい、みたい!取得したresponseをjsonに変換したデータをreturnするだけで、
Promise.メソッドで即座にPromiseを解決(拒否)する場合
// resolve
Promise.resolve(value)
.then(data => {
console.log(data); //
});
// reject
Promise.reject(new Error('エラーが発生'))
.catch(error => {
console.error(error); // エラーメッセージが出力される
});
Discussion