JavaScriptで同期的なwait/sleep/delay関数を作る
ブラウザのDevToolsにコピー&ペーストできるサンプルコード
動作確認は、async/awaitがデフォルトでサポートされているGoogle Chrome推奨です。 1行で書くと次のようになります。
const wait = async (ms) => new Promise(resolve => setTimeout(resolve, ms));
展開型はこうなります。
const wait = async (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(); // setTimeoutの第一引数の関数として簡略化できる
}, ms)
});
}
動作確認
const main = async () => {
console.log(new Date());
await wait(5000); // 5秒待つ
console.log(new Date());
};
await main();
解説
言葉上の表現としては、wait
関数が実行されるとPromiseを生成し、指定した秒数後に解決(resolve)されるのを同期的に(呼び出し側でawait
をつける)待つことで遅延関数を実装しています。
補題1. Promiseチェーンで遅延処理を記述する
async/await
を利用せずにPromiseだけを利用して処理を待機させまう。
console.log(new Date);
const ms = 5000;
new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms)
}).then(() => {
console.log(new Date);
});
補題2. Promiseもasync/awaitも使わずに遅延処理を記述する
console.log(new Date);
const ms = 5000;
setTimeout(() => {
console.log(new Date);
}, ms);
もう少し解説
async/await
とPromise
を知らない場合、補題2のようなコードを書くことになるでしょう。
これはこれで良いのですが、遅延処理をつなげて書きたい場合に破綻します。
俗称コールバック地獄と呼称します。
コールバックのおさらい
コールバック(callback)のおさらいを簡単にしておくと、引数に関数を渡し、関数の実行タイミングを委ねる処理方法です。
const playGame = () => {
console.log("ゲームする");
}
const goHome = (callback) => {
console.log("帰宅した");
callback(); // "帰宅した"あとに`callback`を実行する。callbackについては知らない。
}
goHome(playGame); // callbackとして"ゲームをする"処理を渡す
このコードをブラウザで貼り付けて実行すると
帰宅した
ゲームする
と出力されます。
遅延関数をコールバックで表現する
const wait = (ms, callback) => {
setTimeout(() => {
callback(); // 指定`ms`後にコールバックを実行する
}, ms);
}
パット見良さそうです。それでは、1秒後に"A"、その2秒後に"b"、その3秒後に"c"を出力させる処理を書くと
console.log("start");
wait(1000, () => {
console.log("A");
wait(2000, () => {
console.log("B");
wait(3000, () => {
console.log("C");
});
});
});
となります。ピンとくるまでじっくりと観察してみてください。処理がどんどん増えるたびに階層(コードブロックのネスト)が深くなります。
これは流石に可読性が低くなるため、Promise
やasync/await
が発展していきました(チョット雑)。
長くなってしまったのでここまでにします。