(編集履歴)
日付 | 内容 |
---|---|
2022/3/9 | @standard-software さんからのご指摘を受け、状況説明をより詳細にし、これに合わせてタイトルに文言を追加しました。 |
djangoを使った開発をしていて、「クライアント側からサーバー側にfetchなどでリクエストを送って、時間のかかる処理が終わるのを待ち、処理完了したら1サイクル終了」といった操作を、指定回数だけ順番に繰り返したい…こんな状況に出くわし、理解の浅い非同期処理を実装するのに右往左往してしまったので備忘録。
fetchを使う理由は、サーバーからのリクエストを受け取るときに、画面を再ロードしなくて済むようにしたいので、非同期処理としています。
簡便ですが開発環境は以下。
django: 3.1.4
OS: windows 10
最初にやらかした間違いコード
しょっぱな、「asyncを宣言時につけた関数はPromiseを返してくれる」という文言を見かけて、「じゃあasyncをつけた関数をawaitで待っていれば返り値が来るまで待ってくれるのか…?」と考え、こんなコードを書きました。(大枠だけ示しています)
let count = 3; // 繰り返し回数
func(); // 実行
async function func() {
// 非同期処理を含んだループ
for (let i = 0; i < count; i++) {
await myFunc(); // 処理の完了を待つ
console.log(`${i+1}回目`); // コメント
}
}
async function myFunc() {
fetch('サーバーにリクエストするurl')
.then((response, reject) => {
// 時間のかかる処理を実行
// 処理完了時に目印として「非同期処理終わり」を出力する
});
}
やりたいことと気合は十分伝わってくるコードではあります(たぶん)。
出てきてほしい結果は、
非同期処理おわり
1回目
非同期処理おわり
2回目
非同期処理おわり
3回目
なわけですが、実際に実行してみると、
1回目
2回目
3回目
非同期処理おわり
非同期処理おわり
非同期処理おわり
のように、ループ内のawait myFunc()
はほぼ無視してループはどんどん進んでしまいました。
非同期処理をループさせる方法
というわけであれこれ調べた結果、最終的には以下のようにして非同期処理全体をnew Promise
として返すことで、思い通りの動作を得ることができました。
// ※ myFuncの中身だけ変わっています。
let count = 3; // 繰り返し回数
func(); // 実行
async function func() {
// 非同期処理を含んだループ
for (let i = 0; i < count; i++) {
await myFunc(); // 処理の完了を待つ
console.log(`${i+1}回目`); // コメント
}
}
// 以下変更点
async function myFunc() {
return new Promise((resolve) => {
fetch('サーバーにリクエストするurl')
.then((response, reject) => {
// 時間のかかる処理を実行
// 処理完了時に目印として「非同期処理終わり」を出力する
resolve('返したいものがあれば返す');
});
});
}
参考
以下記事にお世話になりました。
async await の使い方