同期的なコードを書きたい場合・・・
PHPだったら sleep(10) とかすれば 10秒間スリープできます。
でもJavaScriptでは、仕様的な理由でsleep関数が存在しません。
でも作り方がようやく分かったので、
ここでは JavaScriptで sleep関数を実装する方法&コード例 をまとめときます。
このページの目次
JSでのsleep関数作成に必要な3つの知識
まずsleep関数作成には次の3つの知識が必要
- Promise
- アロー関数
- await async
順にどのようなモノなのか、ざっくり説明していきます。
1.Promise(非同期通信を行うオブジェクト)
ではまず Promise について。
これは公式リファレンスだと、こういう説明がされてます。
Promise オブジェクトは非同期処理の最終的な完了処理(もしくは失敗)およびその結果の値を表現します。
このPromise は fetch API でも使われていて、 then() はPromise から受け継いだ感じらしいです。
そこらへんは詳しくないので、ここでは省略します。
たとえば 1.5秒後に非同期で何かするコードを書くなら、こういう感じです。
▼ とても簡単な Promise のコード例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/// 1.5秒後にメッセージを返すPromise var getMsg = new Promise((resolve, reject) => { setTimeout(function(){ resolve('Hello!!'); }, 1500); }); getMsg.then(function(msg){ /// ここが resolve に対応する console.log('msg : ', msg); }, function(){ /// ここが reject に対応する /// 失敗したときの処理 }); |
上コードの
resolve が処理が正しく行われたときに実行したコールバック、
そして
reject が通信エラー時とかの失敗時に実行したいコールバック。
この非同期処理できる Promise が sleep関数実装の1つめのカギです。
2.アロー関数(functionの代替式的なの)
2つめは アロー関数 について
▼ アロー関数のMDNリファレンスでの説明
アロー関数式は、より短く記述できる、通常の function 式の代替構文です。また、this, arguments, super, new.target を束縛しません。アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。
引用元 : アロー関数 - JavaScript | MDN
たとえば () => {} は一番単純なアロー関数の例。
もちろん「通常の function 式の代替構文」なので、 function による置き換えも可能です。
ただしアロー関数は次の利点があるので、使えると便利。
- コードを function式 より短くできる
- 変数をいちいち bind する手間がなくなる
スマートにコードを書くならコチラを使う方がいいかもしれません。
MDNリファレンスでも Promise の説明はアロー関数なので。
3.await async(同期・非同期の制御)
最後は await , async について。
これは関数定義の前につけ、動作を定義するためのキーワードです。
- async
これを関数の前につけると非同期関数(=他の処理を待たない)になる。
たとえば async function hoge(){...} みたいな感じ - await
これは非同期関数の中で使い、それを使うとローカルスコープで処理をブロックする。
たとえば await function fuga() みたいな感じ
文字通り async = 「非同期」、 await = 「待つ」 という意味。
この2つが sleep関数の実装でかなり重要な役割をもってきます。
sleep関数を実装する手順&コード例
では具体的なsleep関数の実装手順について。
今紹介した Promise と await async を使えば簡単にできます。
その手順は次の2ステップ
1.まずミリ秒まで待てる sleep関数 を作成
▼ こんな感じで sleep関数 作ってみた
1 2 3 4 5 6 7 8 |
/// アロー関数を使う場合 var sleep = time => new Promise(resolve => setTimeout(resolve, time)); /// 普通の関数ならこう書き変え可能 function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } |
アロー関数が分かりにくい場合は、普通の function式 でも代替可能です。
2.ウェイトしたい箇所を async で囲み、sleep関数実行
▼ 例えばコード内での書き方例
1 2 3 4 5 6 7 |
(async () => { for(var i = 0; i < 100; i++){ /// 1秒間だけ待つ await sleep(1000); console.log('sleep '+(i+1)+' times'); } })(); |
上コードを実行すると、1秒おきに 'sleep is done' が表示されるはず
▼ 実際のコンソールの様子
同期的になるのは async で囲った部分だけ。
だから sleep(1000) の影響はその外には影響を及ぼしません。
ちなみに少し応用
次みたく 片方は500ミリ秒おき、もう片方は300ミリ秒おき みたいな処理もできます。
▼ 0.5秒 と 0.3秒 おきに2つのタスクを実行するコード例
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(async () => { for(var i = 0; i < 1000; i++){ await sleep(1); console.log('sleep-1 '+(i+1)+' times'); } })(); (async () => { for(var i = 0; i < 1000; i++){ await sleep(3); console.log('sleep-2 '+(i+1)+' times'); } })(); |
上のコードを実行するとコンソールはこんな状態になるはず
まるで他言語でのマルチスレッドみたいな感じ。
まあ JavaScript はシングルタスクなので、厳密にはスレッドとは言えないですが・・・
もし厳密にスレッド実装するなら Web Worker とか使うそうです。(詳しく知らない)
ついでに awati と async の互換性について(蛇足)
最後に蛇足だけど、互換性についての話を少しだけ
まず Promise だと95%以上のブラウザに対応してるから問題なし。
でも await と async って対応ブラウザがどの程度多いのか気になりました。
そこで Can i Use を使って調べてみた結果がコチラ
▼ await のブラウザ互換性
▼ async のブラウザ互換性
簡単に互換性についてまとめると・・・
- await => 2019/11/10 時点で 88.86% に対応、
- async => 2019/11/10 時点で 91.52% に対応
当然ながらIEでは未対応、また一部のモバイルブラウザでも未対応が多そう
でも最新ブラウザやAndroidやiPhoneに対応させる分には問題ないはず
ただし、【IEに何が何でも対応させたい】なら
await と
async は諦めないとダメかも
(日本はやたらとIEが好きだけど、使い続ける理由が良く分からない、、、)
JSでのsleep関数実装まとめ
ということで、実装方法の簡単なまとめ
- Promise
非同期処理用オブジェクトで、この中に setTimeout とかでスリープさせる
- awaitとasync
非同期なコードを async で囲み、 await sleep(1000) のように呼び出せばOK
以上、JavaScriptで同期的に sleep させる方法でした。ではまた(^_^)/~