カテゴリー
【javascript応用編】中断もできてasync/awaitも使える再帰setTimeoutループ処理のPromise版の自作する
※ 当ページには【広告/PR】を含む場合があります。
2022/10/07
課題の再確認〜標準のsetTimeout関数は「非同期割り込み」できない
const ms = 1000; //待機時間(ms単位)
let timerId = setTimeout(function tick() {
//☆再帰的処理の中身(定期実行したい処理を記述)
timerId = setTimeout(tick, ms);
}, ms);
const ms = 1000;
//👇コールバックをasync指定
let timerId = setTimeout(async function tick() {
//...
timerId = setTimeout(tick, ms);
}, ms);
timerId
standard_loop.js
(() => {
let _time = Date.now();
let _count = 0;
const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const waitRandom = async (count) => {
//👇1 ~ 4秒間のランダムな間隔で待機する
const ms = 1000 + Math.random()*3000;
await sleep(ms);
console.log(`[サブプロセス#${count}] 内部処理で ${~~ms}[ms] 遅延が発生`);
};
//👇メインループ処理の間隔は1秒空ける
const loop_ms = 1000;
let timerId = setTimeout(async function tick() {
console.log(`[メインプロセス#${++_count}] 処理を ${~~(Date.now() - _time)}[ms] 経過後に開始`);
_time = Date.now();
await waitRandom(_count);
timerId = setTimeout(tick, loop_ms);
}, loop_ms);
//👇プログラム開始10秒後にタイマーを終了する
setTimeout(() => {
console.log(`[メインプロセス] 外部処理でタイマーを終了!`);
clearTimeout(timerId);
}, 10000);
})();
[メインプロセス#1] 処理を 1003[ms] 経過後に開始
[サブプロセス#1] 内部処理で 2168[ms] 遅延が発生
[メインプロセス#2] 処理を 3170[ms] 経過後に開始
[サブプロセス#2] 内部処理で 2565[ms] 遅延が発生
[メインプロセス#3] 処理を 3569[ms] 経過後に開始
[サブプロセス#3] 内部処理で 1404[ms] 遅延が発生
[メインプロセス] 外部処理でタイマーを終了!
Done in 16.24s.
[メインプロセス#1] 処理を 1003[ms] 経過後に開始
[サブプロセス#1] 内部処理で 3197[ms] 遅延が発生
[メインプロセス#2] 処理を 4201[ms] 経過後に開始
[サブプロセス#2] 内部処理で 3095[ms] 遅延が発生
[メインプロセス#3] 処理を 4100[ms] 経過後に開始
[メインプロセス] 外部処理でタイマーを終了!
[サブプロセス#3] 内部処理で 2156[ms] 遅延が発生
[メインプロセス#4] 処理を 3158[ms] 経過後に開始
...以下省略
非同期割り込みにも使える再帰setTimeoutタイマーのPromise対応させる
promise_loop.js
(() => {
//👇外部からタイマーを停止させるためのフラグ
let stopFlg = false;
let _time = Date.now();
const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const waitRandom = async (count) => {
//👇1 ~ 4秒間のランダムな間隔で待機する
const ms = 1000 + Math.random()*3000;
await sleep(ms);
console.log(`[サブプロセス#${count}] 内部処理で ${~~ms}[ms] 遅延が発生`);
};
//👇ループ処理の完了を受け取るまでのPromiseを返す関数
function subscriber(i = 0) {
const loop = (count) => {
return new Promise(async (resolve) => {
//👇メインループ処理の間隔は1秒空ける
const loop_ms = 1000;
await sleep(loop_ms);
console.log(`[メインプロセス#${count}] 処理を ${~~(Date.now() - _time)}[ms] 経過後に開始`);
//👇ループ処理の中身(非同期処理も可能)
await waitRandom(count);
_time = Date.now();
//👇次回のチェーンループ処理の呼び出しで利用する
resolve(count+1);
})
}
return new Promise((resolve) => {
//👇チェーンループ処理(再帰的に呼び出しに相当)
loop(i).then((count) => {
//👇外部からのループ処理を停止させる条件
if (!stopFlg) {
//👇別のPromiseを使ってチェーンループに処理が続く
subscriber(count).then((res) => resolve(res));
} else {
//👇Promise自体のresolveで解決させるとループ処理が終了
resolve('[サブプロセス] 内部処理でタイマーも終了');
}
})
})
}
//👇ループ処理開始
subscriber(1).then((msg) => {
//👇ループ処理の終了メッセージを受け取る
console.log(msg);
});
//👇プログラム開始10秒後にタイマーを終了する
setTimeout(() => {
console.log(`[メインプロセス] 外部処理でタイマーを終了!`);
stopFlg = true;
}, 10000);
})();
[メインプロセス#1] 処理を 1002[ms] 経過後に開始
[サブプロセス#1] 内部処理で 2934[ms] 遅延が発生
[メインプロセス#2] 処理を 3937[ms] 経過後に開始
[サブプロセス#2] 内部処理で 2538[ms] 遅延が発生
[メインプロセス#3] 処理を 3542[ms] 経過後に開始
[サブプロセス#3] 内部処理で 1091[ms] 遅延が発生
[メインプロセス] 外部処理でタイマーを終了!
[メインプロセス#4] 処理を 2093[ms] 経過後に開始
[サブプロセス#4] 内部処理で 3653[ms] 遅延が発生
[サブプロセス] 内部処理でタイマーも終了
subscriber
then
then
メソッドチェーン
resolve
reject
subscriber
まとめ
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー