情報源: Publicis フロントエンド・フルスタック開発者
JavaScriptには、実行を再開する前に指定した時間だけコードを待機させるsleep()関数がありません。JavaScriptに待機が必要な場合はどうすればよいですか?
JavaScriptにはsleep()メソッドがないので、次善の策としてsetTimeout()を使ってみましょう。
残念なことに、setTimeout() は使い方によっては期待通りに動作しません。おそらく、JavaScriptのループのどこかで試してみて、setTimeout()がまったく機能しないように見えるのを見たことがあるでしょう。
この問題は、setTimeout()をsleep()関数と誤解していることに起因します。
この記事では、setTimeout()の使い方を説明します。setTimeout()を使って、JavaScriptの実行を一時停止し、連続したコードの行間を待機させるsleep関数を作る方法も含めて説明します。
setTimeout()のドキュメントを読むと、ミリ秒単位の "delay "パラメータを取るようです。
元の問題に戻ると、console.log()関数を呼び出す間に1秒待つためにsetTimeout(1000)を呼び出してみました。
残念ながら、setTimeout()はそのようには動作しません:
setTimeout(1000)
console.log(1)
setTimeout(1000)
console.log(2)
setTimeout(1000)
console.log(3)
for (let i = 0; i <= 3; i++) {
setTimeout(1000)
console.log(`#${i}`)
}
このコードでは、setTimeout()が存在しないかのように、まったく遅延が発生しません。
ドキュメントを見直すと、問題は最初の引数が実際には遅延ではなく関数呼び出しであるべきだということがわかります。結局のところ、setTimeout()は実際にはsleep()メソッドではありません。
コールバック関数を最初のパラメータに、必要な遅延を2番目のパラメータに含めるようにコードを書き換えます:
setTimeout(() => console.log(1), 1000)
setTimeout(() => console.log(2), 1000)
setTimeout(() => console.log(3), 1000)
for (let i = 0; i <= 3; i++) {
setTimeout(() => console.log(`#${i}`), 1000)
}
こうすることで、3つのconsole.logのログメッセージは、1000ミリ秒の遅延の後にまとめて表示されます。
この問題を解決する方法を説明する前に、setTimeout()関数を詳しく調べてみましょう。
setTimeout () をチェックします。
上の2番目のコード・スニペットで矢印関数が使われていることにお気づきでしょうか。これは、タイムアウト後に実行されるコードを実行する setTimeout() に匿名コールバック関数を渡す必要があるためです。
匿名関数では、タイムアウト時間後に実行される任意のコードを指定できます:
// 矢印構文を使った匿名コールバック関数
setTimeout(() => console.log(" !"), 1000)
// これは、関数キーワード
setTimeout(function() { console.log(" !") }, 1000)
理論的には、関数を最初の引数として渡し、コールバック関数の引数を残りの引数として渡せばよいのですが、私の場合は正しく動作したことがありません:
// 動くはずだが、動かない
setTimeout(console.log, 1000, " ")
// 動かないはずなのに、動く
setTimeout(`console.log(" ")`, 1000)
では、なぜ最初のコード例ではsetTimeout()が失敗するのでしょうか?これは正しく使用されているようで、毎回1000ミリ秒の遅延を繰り返しています。
その理由は、setTimeout() が同期コードとして実行され、setTimeout() への複数の呼び出しが同時に実行されるためです。setTimeout()の各呼び出しは、指定された遅延の後に後で実行される非同期コードを作成します。コード・スニペット内の各遅延は同じであるため、キューに入れられたコードはすべて、1 秒の遅延の後に同時に実行されます。
前述したように、setTimeout() は実際には sleep() 関数ではありません。幸運なことに、setTimeout()を使ってJavaScriptで独自のsleep()関数を作成することができます。
スリープ関数の書き方
Promises関数、async関数、await関数を使えば、期待通りに実行されるsleep()関数を書くことができます。
ただし、このカスタムsleep()関数を呼び出せるのは非同期関数の中だけで、awaitキーワードと一緒に使う必要があります。
このコードは sleep() 関数の書き方を示しています:
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay))
const repeatedGreetings = async () => {
await sleep(1000)
console.log(1)
await sleep(1000)
console.log(2)
await sleep(1000)
console.log(3)
}
repeatedGreetings()
このJavaScriptのsleep()関数は、awaitによってPromiseが解決されるまでコードの同期的な実行を一時停止させるので、あなたが期待することを正確に実行します。
シンプルな選択
あるいは、最初に setTimeout() を呼び出すときにタイムアウトを増やすように指定することもできます。
次のコードは前の例と同じです:
setTimeout(() => console.log(1), 1000)
setTimeout(() => console.log(2), 2000)
setTimeout(() => console.log(3), 3000)
コードが同時に実行されるため、指定されたコールバック関数は、同期されたコード実行の1秒後、2秒後、3秒後に実行されます。
ループしますか?
ご想像の通り、JavaScriptの実行を一時停止する上記のオプションは、どちらもループの中で問題なく動作します。2つの簡単な例を見てみましょう。
これは、カスタムsleep()関数を使用するコードスニペットです:
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay))
async function repeatGreetingsLoop() {
for (let i = 0; i <= 5; i++) {
await sleep(1000)
console.log(`Hello #${i}`)
}
}
repeatGreetingsLoop()
以下は、「タイムアウトを増やす」を使った簡単なコードです:
for (let i = 0; i <= 5; i++) {
setTimeout(() => console.log(`Hello #${i}`), 1000 * i)
}
私は後者の構文を好みます。
要約すると
JavaScriptにはsleep()関数やwait()関数がないかもしれませんが、組み込みのsetTimeout()関数を使えば、使い方にさえ気をつければ簡単に作ることができます。
setTimeout()自体はsleep()関数として使用することはできませんが、asyncとawaitを使用してカスタムJavaScript sleep()関数を作成することができます。
別のアプローチを使用すると、sleep()関数をエミュレートするために、 setTimeout()にインターリーブされたタイムアウトを渡すことができます。これは、setTimeout()へのすべての呼び出しが、JavaScriptが通常そうであるように、同期的に実行されるために動作します。
うまくいけば、外部ライブラリやフレームワークを使わずに生のJavaScriptだけを使って、あなたのコードに遅延を導入するのに役立つでしょう。
楽しくコーディングしましょう




