JavaScriptのasync/awaitが動かないときの対処方法
JavaScriptの勉強を始めて非同期処理でつまづきました。ようやく理解できて来たので、非同期処理についてまとめました。実装していて動かないということが多々あるので、原因と対策も一緒に記載しています。
async/awaitについてのおさらい
非同期処理とは?
非同期処理とは、ある処理の完了を待たずに他の処理を実行することです。反対に処理を1つずつ実行することを同期処理と言います。
ネットワーク通信やファイル入出力など実行完了までに時間がかかる重たい処理を同期処理で実行すると、重たい処理が終わるまでUIが固まって見えるため、重たい処理は非同期処理で実行するのが普通です。
JavaScriptはシングルスレッド
JavaScriptはシングルスレッドで動作します。シングルスレッドのため並列処理のできないJavaScriptで非同期処理を行うために考え出されたのが、Promiseとasync/awaitです。
Promiseとは?
PromiseはJavaScriptの非同期処理を理解するうえで重要です。async/awaitを使っていてもPromiseの理解は必要です。
Promiseは2つのコールバック関数を引数にとる関数です。2つのコールバック関数は、resolve
とreject
です。resolve
はPromise内の処理が成功したときに呼ばれるコールバック関数で、reject
は失敗したときに呼ばれるコールバック関数です。
Promiseの特徴としてthen/catchをチェーン状につなぐことで非同期処理をあたかも同期処理のように実装できる点があります。
new Promise((resolve, reject) => {
// 処理1
const result = heavyFunc();
if (result) {
// 成功時
resolve(result);
} else {
// 失敗時
reject(result);
}
}).then((ret) => {
// 処理2
}).catch((ret) => {
// 処理3
});
Promiseは待たない
Promiseは処理の完了を待たない(ブロックしない)ため、次の処理が実行されます。Promiseは「実行が完了したときに、resolve
またはreject
を呼び出します。」という約束をするものです。
async/awaitとは?
asyncは非同期処理の関数につけます。非同期処理が完了した場合はreturn
で値を返し、失敗した場合はthrow
でエラーを返します。Promiseと比べるとresolve
=return
、reject
=throw
という関係になります。
try {
async function myFunc() {
// 処理1
const result = heavyFunc();
if (result) {
// 成功時
return result;
} else {
// 失敗時
throw result;
}
}
const ret = await myFunc();
// 処理2
} catch(ret) {
// 処理3
});
Promiseと同様、asyncは非同期処理ですので処理をブロックしません。そうです。asyncはPromiseを返すのです。一方、awaitはPromiseが結果(成功or失敗)が変えるのを待ちます。
async/awaitが動かないときの対処
awaitが待たずに次の処理が実行される
async内の関数で別な非同期処理を呼んでいて、かつその非同期処理をawaitしていない場合、非同期処理の完了を待たずに次の処理が実行されてしまいます。
async function myFunc() {
otherAsyncFunc(); // awaitの付け忘れ
return;
}
await myFunc();
別な原因として、async内の関数で非同期処理を呼んでいて、かつそのコールバック関数の外でresolve/rejectを実行している場合も、非同期処理が完了していないのに完了したとみなされて次の処理を実行してしまいます。
async function myFunc() {
setTimeout(()=>{
// タイムアウトしたときの処理
}, 1000);
resolve(); // setTimeout()の中にあるべき
}
await myFunc();
await以降の処理が実行されない
Promise内でresolveの書き忘れたり、async内でreturnを書き忘れたりすると、非同期処理の完了がされないため、await以降の処理が実行されません。
文法エラーが発生する
awaitはトップレベルもしくはasync関数内でしか実行できません。awaitを呼んでいる関数にasyncがついていない場合は、asyncをつけます。
then/catchがアロー関数になっていない
以下のように、thenやcatchがアロー関数になっていない場合、処理1が完了する前に処理2、処理3が実行されてしまいます。
new Promise((resolve, reject) => {
// 処理1
}).then(
// 処理2
).catch(
// 処理3
)
参考サイト
- JavaScriptの非同期処理解説シリーズ
- プログラミングアカデミーさんの動画です。解説が非常にわかりやすい。
最後まで読んでいただきありがとうございます。
また読んでくださいませ。
そんじゃーね。