【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
を呼び出したいとき
setTimeout
fetch
- データベース操作
- ファイル読み書き,など
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