非同期処理 async/await
分かりやすさ重視、所々本質とは違うと思います。理解の備忘録としてご了承ください。
同期処理と非同期処理
コードを実行する際に
同期処理:上から順に実行される通常の実行
非同期処理:一部処理を退避させて別処理させる。
Promiseについて
Promise:関数の返り値としてアタッチされる、関数の種類、関数の返り値の型と思ってもいいかも。処理の実行を約束する。失敗やその途中経過や結果を返す。
返される結果について
- pending(実行中)
- settled(実行完了)
→settled(実行完了)の中身- rejected(失敗)
- fulfilled(成功)
・Promise のコンストラクタ の 第一引数 / 第二引数 コールバック について
const promise = new Promise(function(resolve, reject)=>{
if (True) {
resolve("resolveの時の内容");
} else {
reject("rejectの時の内容");
}
console.log("関数は止まらない");
});
promise
.then((value) => console.log("resolveの内容:", value))
.catch((reason) => console.error("rejectの内容:", reason));
Promiseのコンストラクタの 第一引数 / 第二引数 に(resolve,reject)を設定し
関数内に
成功時(fulfilledの内容)→resolve(“返したい内容“)
失敗時(rejectedの内容)→reject(“返したい内容“)
を記述する。
なお、resolve,rejectを通過しても関数の実行自体は止まらない。
asyncについて
関数の頭につけると返り値が強制的にpromiseとなる。
asyncの内部の関数はawaitが使用できるようになる。
実行の結果を待ちたいとき
基本的にPromise/then or async/awaitの組み合わせが多い。
※thenとawaitを同居させることもできるが最初のうちは使わない方が理解がしやすいはず。
Promise/then使用時
Promise/thenで繋いで記述(※同様の概念がawait。then≒await)
//axiosはapi通信に関するライブラリで、axios.postは返り値がpromiseとなる関数
axios.post("url~~~~~~")
.then((res)=>{
console.log(res);
const test = axios.post(res.url)
return test
})
.then((test)=>{
console.log(test)
})
async/await使用時
async/await を用いて記述。awaitを使うときはasyncとセット。awaitはasync関数の中身でしか使用できない。
await の後ろにはpromiseを返す関数がくる( await {promise関数})。
余談)async自体が関数の返り値をpromise化するのに対して、awaitの後ろにもPromise関数を置かないといけないのイメージしづらい(入れ子みたいになる)。まあawaitみたいに実行に時間がかかって待たせたい処理は大体Promise的な処理だし、そうじゃなくてもPromise化しときたいよねって感じ。
await以降の記述はawaitの処理が終わるまで実行されない。(thenと同じイメージ。)
言い換えるとawaitで一旦止まる。
//このfunction Testの頭にasyncをつけたおかげでTest関数の返り値はpromiseとなる。
async function Test()=>{
//axiosはapi通信に関するライブラリで、axios.postは返り値がpromiseとなる関数
const res = await axios.post("url~~~~~~")
console.log(res);
const test = await axios.post(res.url)
console.log(test)
}
//上記では関数を宣言しただけなので別途実行する必要あり
Test()
こっちの方がスッキリ書ける。
Promise.all() promise系の同時処理を行いたい場合
複数のPromise系処理を同時に行いたい場合(並列処理したい場合)はPromise.allを使う。
Promise.all( [ promise関数1,promise関数2,… ] )
例えばapi通信を5個同時に行いたい場合など
await axios.post(URL1)
await axios.post(URL2)
await axios.post(URL3)
await axios.post(URL4)
await axios.post(URL5)
とすると一々実行完了まで待つのでかかる時間は
URL1 + URL2 + URL3 + URL4 +URL5
となるがPromise.allを使うと
Promise.all( [ axios.post(URL1), axios.post(URL2),axios.post(URL3),axios.post(URL4),axios.post(URL5) ] )
URL1〜5のpost通信を同時に行う(並列処理する)ため、
かかる時間はURL1~5のうち最も長い時間のみになる。(MAX(URL1,…,URL5))
エラーハンドリングは.thenと.catchで行う。
Promise.all(promise関数1,promise関数2)
.then((data)=>{
//promise関数1の結果
console.log(data[0])
//promise関数2の結果
console.log(data[1])
console.log("全て成功")
})
.catch((err)=>{
console.log("実行したうちのどれか一つでも失敗")
})
結果はそれぞれthenの引数に格納されてたりする。const data = Promise.all()とするとデータを外に取り出せたりする。
その他
歴史的な流れ
非同期処理の歴史的には
setTimeoutによるcallback地獄
→promise / thenが 発明されて可読性向上
→async / await によりさらに可読性向上
のイメージ。
参考記事