javascriptでは、処理(スレッド)を一時停止させるsleep関数が存在しません。
他の言語では珍しくない処理ですが、自分で組む必要があります。
今回は実装例とその解説を掲載しました。
また、すぐに使えるコードも用意しています。
とにかく今すぐ使いたい方はそのままご利用ください。
短縮コードに慣れていないと読みづらい可能性があります。
ですが比較と分解をしながら解説をしていますので、ご安心ください。
特に参考にしてほしい方
- javascriptでsleepを今すぐに使いたい方
- sleep処理の実装方法を知りたい方
- 何度も調べなおさないよう、一発で理解したい方にもおすすめ!
注意ポイント
ES2017以降が対象です。
例によってIEでは動きません。
しかし、windows系でもWindowsServer以外ではサポートが切れています。
ほとんどの場合は、問題になることはないでしょう。
すぐに使いたい方へ!コピペで実装できるサンプルコード!
// 関数
const my_sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// 呼び出しサンプル
await my_sleep(3000);
非同期処理「Promise」の基本をコードを使って解説
メインの関数では、Promiseという非同期処理を定義しています。
宣言だけでは秒数がずれてしまうので、呼び出し側で同期が必要です。
コードでいう「await」が、Promiseに対する同期命令になります。
動作自体は、ループで待機させてもあまり変わりません。
しかし、ループに比べ待機中のcpu消費を小さくできます。
また、「await」は非同期関数内でしか使用できません。
以下のコードを参考にしてください。
const test = async () => {
await my_sleep(3000);
};
test();
「test」という関数名の前に、「async」とあります。
この「async」が非同期関数の宣言方法です。
非同期処理は、非同期関数のみでしか使用できません。
思わぬエラーを招くので、結構重要なルールです。
実際にサンプルプログラムを使って、動作を確認してみました。
/**
* ※本筋とは無関係, 経過時間を[ms]で取得
*/
const get_time = () => {
return new Date();
};
const my_sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const test = async () => {
const before = get_time();
await my_sleep(3000);
console.log({ type: 'シンプルawait版', diff: get_time() - before });
};
test();
画像はChromeのコンソールをキャプチャしたものです。
単位はミリ秒なので、約三秒間経過している事が分かります。
正確には「3000」ミリ秒なので、多少ずれていますね。
タイマーの誤差や出力までのラグがあるため、ぴったりにはならない可能性があります。
短縮コードを分解して、コードを意味を理解しよう
上のコードには短縮テクニックが豊富です。
慣れていない場合は、なんとなく分かる程度の理解かもしれません。
理解をより深めるため、コードを分解していきましょう。
ラムダ式(アロー関数)のテクニックについて
今回は、まず先にテストコードの全体像を掲載します。
/**
* ※本筋とは無関係, 経過時間を[ms]で取得
*/
const get_time = () => {
return new Date();
};
const my_sleep = (ms) => {
return new Promise((resolve) =>
setTimeout(() => {
resolve();
}, ms)
);
};
const test = async () => {
const b = get_time();
await my_sleep(3000);
console.log({ type: "await 冗長版", distance: get_time() - b });
};
test();
ラムダ式(アロー関数)がどう使われているかを理解しよう!
const my_sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
このアロー関数から、中括弧を外してみます。
const my_sleep = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
};
見慣れた構文に近づいてきました。
アロー関数には、次のような特徴があります。
一行で完了する処理は、中括弧とreturn文を省略することができる。
もちろん、アロー関数を使わず通常の関数で書くこともできます。
const my_sleep = function (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
};
ここまでは、アロー関数に着目して解説してきました。
実はもう一つ、Promiseのタイマー処理に関して工夫されています。
次のセクションでは、Promiseの工夫について理解を深めましょう。
Promiseのテクニックについて
今回も元のコードと比較してみましょう。
new Promise(resolve => setTimeout(resolve, ms));
これは以下のように解釈できます。
new Promise((resolve) =>
setTimeout(() => {
resolve();
}, ms)
);
まずPromiseは、引数としてresolve, rejectの2つの引数を取ります。
※今回使っているものは、エラーハンドルが不要なためresolveのみで構いません。
ここでは、次のような処理が行われています。
- Promiseのコールバックで、setTimeoutを呼び出し
- setTimeoutのコールバックで、resolveの通知を出す
そのため、下記のような構文は間違いです。
間違い1 resolveを呼んでいない => Promiseが解決(完了)していないとみなされる
new Promise((resolve) =>
setTimeout(() => null, ms)
);
=> 出力なし
間違い2 setTimeoutでresolveを直接実行させてしまう
// 主に短縮版で注意
new Promise(resolve => setTimeout(resolve(), ms));
すぐに関数が実行されてしまいawaitが効きません。
async, awaitを使わない場合はPromiseの基本に立ち返ろう
Promiseで同期する場合は、async関数内でなければならないことは確認しました。
では何らかの制約でasyncを使えない場合は、どうすればいいでしょうか。
その時は、Promiseの「then」メソッドを使います。
構文としては、jQueryのAjaxに近いです。
使ったことがあれば、イメージしやすいのではないでしょうか。
// 呼び出し側を変更
my_sleep(3000).then(() => {
console.log({ type: "callback版", distance: get_time() - b });
});
処理の流れ
Promiseの戻り値に対して、メソッドチェーンで繋いでthen()を定義します。
最初の引数に、Promiseがresolveを通知した際のコールバックを渡しましょう。
この方法では、ネストが深くなりがちです。
awaitが使える場面では、基本的に採用することをおすすめします。
そもそもjavascriptでsleepは必要なのか
標準で提供されていない以上、積極的に使う場面はないと思います。
async, await, そしてコールバックがあればなんとかなるものです。
可能性があるとすれば、外部APIと連携する時などでしょうか。
外部API連携の使用例
更新処理が絡む場合は、「リクエスト処理中」で返す場合も多いです。
実際にCMSのRESTAPIで経験したことがあります。
1ページにつき10秒かかるなら、10~15秒くらいインターバルをとればOKです。
※実際はネットワークやAPIサーバー次第で、振れ幅があります。
sleepは簡単なものの、実用性としてはイマイチです。
本番では、setTimeoutなどを使いながらfetchAPIを叩いた方が確実でしょうね。
[まとめ]javascriptでsleep機能は結構簡単に作れる
ES2017以降の機能を使えば、結構簡単に実装できることが確認できたと思います。
一方でラムダ式やPromiseを駆使しているなど、思ったより?高度なことをしています。ES2015の浸透が遅れたのはIEのせいです。
javascriptで遅延動作をさせるときは、
- 同期処理(async, await)で完了を受け取れないか検討する
- コールバックで解決できないか検討する
- それでもダメならsleepで対処する
くらいの優先度が良いと思います。
参考
Promise
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
アロー関数(ラムダ式)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions