概要
同一のURLにリクエストを送り続けるという事は中々ないのだけれど、色々あって作らなければいけなくなったので備忘録として書いておく。具体的には意図した結果が返るまで延々と非同期処理を処理を送り、なおかつレスポンスを待っている間はリクエストを投げずに待つというもの。
ジェネレータ関数
詳細はMDNの該当ページを参照。ジェネレータ関数の使い方自体はやめ太郎さんの記事の方が分かりやすい。要するに呼び出される度に次のyieldまでの処理を行う関数なのだが、当然反復処理を使うと呼び出すたびに処理を実行する。しかもasync/awaitも使える。
while(true)にすると無限に非同期処理を実行し続けるが、今回は念のため実行回数を指定するようにし、指定した回数に達したらnullを返すようにしてある。
export async function* polling(request: () => Promise<any>, limit = 5) {
if (typeof request !== "function") {
throw new Error("callback must be a function.");
}
let pollingCounter = 0;
// 実行するとwhileの中に入る。
while (pollingCounter < limit) {
pollingCounter++;
console.log(pollingCounter)
yield await request();
}
yield null;
}
for await of で反復処理を行う。
普通のfor文 やforeachは非同期処理を無視する。そのため非同期処理を反復実行したいときはfor await ofを使う。(3/15追記: 普通のfor文はasyncawait対応でした。)
MDNの該当ページ曰く、
非同期の反復可能プロトコルを実装している非同期ジェネレーターであれば、 for await...of を使用して繰り返し処理を行うことができます。
なのでfor ofならオブジェクトや配列が入る場所にジェネレータ関数を入れることができる。
import { polling } from './request'
async function api(){
// なんか非同期の処理
}
const remind = async (count:number = 5): Promise<void> => {
// ここにジェネレータ関数
for await (let result of polling(api, count)) {
// 試行回数を超えるとnullが返るため、ここで処理を止める
if (result === null) {
break;
}
if (result) {
// ここでレスポンスを使った処理
break;
}
// 繰り返す場合はbreakしない。
}
// これでジェネレータを止める
polling.return()
}
結局何に使ったの?
NFCを読み込むアプリのフロントエンド。
バックエンドはFastAPI+nfcpy+uvicornの構成で、リクエストするとNFCリーダーを5秒読み込み待機状態にするAPIを実装している。
試行回数を変えることで見た目上の待機時間を制御できるようにしたいので、非同期処理を直列で処理する必要があった。
アプリについては気が向いたら書きたい。
注意
ジェネレータ関数もfor await ofもIE非対応です。