Search

検索したいワードを入力してください

2019年02月28日

sleepのないJavaScriptで一時停止処理を実装する

JavaScriptでsleepを実現する方法を解説いたします。JavaScriptにはsleep関数が用意されていないため、自前で実装する必要があります。setTimeoutやPromiseなどを組み合わせる必要があるので順を追って解説いたします。

sleepとは

sleepはプログラムの処理を一定時間止める命令です。

例えばタイマー機能を実現する際に使います。sleep命令を使うことで何分後にアラームを出すといった機能を実現できます。

JavaScriptでsleepはできない

タイマー機能の実現に必要なsleep命令ですが、JavaScriptにsleep命令は備わっていません。JavaやC、Pythonなどの多くの言語でsleep命令は用意されているのですが、JavaScriptにsleep命令は用意されていません。

参考まで、例えばJava言語の場合はThread.sleep(long milliseconds)関数を用いて処理を一定時間止めることができます。

// Java言語のsleep処理のサンプルコード
System.out.println("5秒後にお知らせします。");
Thread.sleep(5000);
System.out.println("5秒立ちました。");

JavaScriptでsleep処理を実装する方法

JavaScriptにsleep命令は備わっていませんが、JavaScriptでもタイマー機能、ジェネレータ、Promiseなどを用いてsleep機能は実現できます。それぞれの方法を順番に解説いたします。

JavaScriptのsetTimeout関数を使ってsleep機能を作る

JavaScriptでsleepを実現する方法としてsetTimeout関数を使う方法があります。

setTimeout関数は一定時間後に特定の処理を起動する命令です。

setTimeout関数の使い方は以下のようになります。


setTimeout( /* タイムアウト後に呼び出す関数 */ , /* タイムアウト時間(ミリ秒) */ );

【サンプルコード】

// setTimeoutによるsleep処理のサンプルコード
var callback = function(){
window.alert("5秒立ちました。");
}

window.alert("5秒後にお知らせします。");
setTimeout(callback, 5000);

御覧のとおり、setTimeoutを使うことでJavaScriptでもsleepが実現できました。しかし、setTimeoutはsleep後の処理をコールバック関数で行う必要があり、sleepの前後で処理の記述が分断されてしまいます。

Javaの記載のように処理の途中にsleepを記載した方が処理の流れが分かり易くなるので、JavaScriptでsleep処理をJavaのように直列で記載する方法を順を追って解説いたします。

// これから実現したいこと
window.alert("5秒後にお知らせします。");
sleep(5000); // ← 処理の途中にsleepを記載したい
window.alert("5秒立ちました。");

JavaScriptのジェネレータを使ってsleep機能を作る

ジェネレータとsetTimeout関数を併用することでコールバック関数を作らずにsleep処理が行えます。

まず、ジェネレータについてご説明いたします。ジェネレータはECMAScript 6でJavaScriptに追加された機能で日本語にすると生成器になります。ジェネレーターはfunction* ジェネレータ名() { 処理 }で定義します。どういうものか具体的に見てみましょう。

// 1~5の数字を生成するジェネレータを定義する
function* idMaker(){
var i = 1;
while(i <= 5)
yield i++;
}

// ジェネレータを生成する。
var gen = idMaker();

// ジェネレーターを使い数字を順番に生成する
window.alert(gen.next().value); // 1
window.alert(gen.next().value); // 2
window.alert(gen.next().value); // 3
window.alert(gen.next().value); // 4
window.alert(gen.next().value); // 5

ジェネレータのnext()関数を実行すると、ジェネレータ定義のyieldまで処理が行われ、yieldの値がnext()関数に返されます。再度next()関数を実行すると前回のジェネレータ定義のyieldの次から処理が再開され、次のyieldまで処理が行われます。

ジェネレータ関数内のyieldを使う

ジェネレーターは前述のように連番などを生成する時に用いられる機能になりますが、yield処理まで処理が行われ処理が中断する特性を用いて、sleep処理を直列に記載できます。

【サンプルコード】

function sleep(g, timeout) {
setTimeout(function() { g.next(); }, timeout);
}

var mainlogic = (function *() {
window.alert("5秒後にお知らせします。");
yield sleep(mainlogic, 5000);
window.alert("5秒立ちました。");
})();

mainlogic.next();

JavaScriptのPromise文を使ってsleep機能を作る

PromiseとsetTimeout関数を併用することでもコールバック関数を作らずにsleep処理が行えます。

まず、Promiseについてご説明いたします。PromiseもECMAScript 6でJavaScriptに追加されたもので、非同期処理を直列にプログラミングするための機能です。どういうものか具体的に見てみましょう。

分かり易いように非同期処理の結果を受けて次の処理を行うプログラムを、JavaScript従来のコールバック関数を使う方法とPromiseを使う方法で記述します。

【コールバック関数を使う方法】

// 非同期処理
function asyncFunction(callback) {
window.alert("1.非同期処理");
callback("Async Success");
}

// 非同期処理を実行する
setTimeout(asyncFunction,
1,
function(result){
// コールバック処理。この処理は非同期処理の後に呼ばれる。
window.alert("2.非同期処理後の処理。return message:" + result);
}
);

【Promiseを使う方法】

// 非同期処理
function asyncFunction() {
// Promiseオブジェクトを返却する。処理成功時にはresolveが呼ばれる
return new Promise(function (resolve, reject) {
setTimeout(function() {
window.alert("1.非同期処理");
resolve("Async Success");
}, 1000
);
});
}

// 非同期処理を実行する
asyncFunction().then(
function (result) {
// コールバック処理。この処理は非同期処理の後に呼ばれる。
window.alert("2.非同期処理後の処理。return message:" + result);
}
);

コールバック関数を使った場合の記載ですと、"1.非同期処理"と"2.非同期処理後の処理"の順番が読み取れませんが、Promiseを使った場合は、"1.非同期処理"と"2.非同期処理後の処理"が直列に動くことがコードから読み取れます。

return new Promiseを使う

Promiseは前述のようにJavaScriptで非同期処理を直列に記載するための機能になります。
Promiseの非同期処理を直列に記載できる特性を用いて、sleep処理をコールバック関数を作らずに実装ができます。

【サンプルコード】

function sleep(timeout) {
return new Promise(function (resolve, reject) {
setTimeout(function() { resolve(""); }, timeout); });
}

window.alert("5秒後にお知らせします。");
sleep(5000).then(
function(){ window.alert("5秒立ちました。"); } );

asyncとawaitを使ってJavaScriptでsleep機能を作る

async/awaitとPromise、setTimeout関数を併用することで、よりシンプルにsleep処理が行えます。

まず、async/awaitについてご説明いたします。asyncとawaitはECMAScript(ES2017 / ES8)でJavaScriptに追加された機能です。asyncはPromiseを簡潔に記載するもので、awaitはPromiseがresolveを行うまで待機する命令になります。どういうものか具体的に見てみましょう。

【async/awaitのサンプルコード】

function innerMethod() {
return new Promise(function(resolve, reject) {
setTimeout(function() {resolve("test"); }, 2000);});
}

// 非同期処理をPromiseを使わずに記述
async function sample() {
window.alert("これは非同期処理です。Promiseと同じです。");
const result = await innerMethod(); // innerMethodを呼び出し、innerMethodがresolveを行うまで処理を待機する
return "success";
}

// 非同期処理を実行
sample().then(function(result) {
window.alert(result);
});

少し詳しく解説いたします。asyncの部分はPromiseで置き換えることができます。

【asyncでプログラムした場合】

async function sample() {
window.alert("これは非同期処理です。Promiseと同じです。");
const result = await innerMethod();
return "success";
}


【Promiseでプログラムした場合】

function sample() {
return new Promise(function(resolve, reject) {
window.alert("これは非同期処理です。Promiseと同じです。");
const result = await innerMethod();
resolve("success");
);
}

asyncが宣言された関数は、戻り値としてPromiseを返します。asyncが宣言された関数のreturn文は関数の戻り値の返却ではなく、resolveを実行したことになります。

awaitはPromiseを返す関数を実行し、呼び出した関数のPromiseがresolveを行うまで待機する機能になります。

async function内のawaitを使う

asyncとawaitを利用することでPromiseを使用した時よりもシンプルにsleep処理を実装できます。

【サンプルコード】

function sleep(timeout) {
return new Promise(function (resolve, reject) {
setTimeout(function() { resolve(""); }, timeout); });
}

(async function() {
window.alert("5秒後にお知らせします。");
await sleep(5000);
window.alert("5秒立ちました。");
}());

ビジーwaitを利用したsleep機能を作る

sleep機能を実現するもう一つの方法として、while()ループを指定秒の間「空ループ」させるやり方があります。ただし、whileの空ループでsleepを実現する場合、常にCPUを消費してビジー状態になります。

平行して動かすアプリケーションがある場合は、平行して動いているアプリケーションも遅くなるので利用する際は注意が必要です。実装方法がシンプルのため1つの手法としてご紹介いたします。本番では使わないように注意してください。

while文の中にdateを取得する

空ループでsleepを実現するにはwhile文の開始時とwhile文の中でdate関数を使い実行時間を取得し、sleep時間を判定します。

【サンプルコード】

function sleep(timeout) {
var startTime = new Date();
while (new Date() - startTime < timeout) {
// 空ループ
}
}

window.alert("5秒後にお知らせします。");
sleep(5000);
window.alert("5秒立ちました。");

jQueryを使ってsleep機能を作る

jQueryのDeferredオブジェクトを使うことでもsleep機能が実現できます。

jQueryのDeferredオブジェクトはjQueryでPromiseを実現する方法で、実装の基本構成はPromiseを使用した場合と変わりません。

jQueryのDeferredオブジェクトでsleepを実現する方法は以下となります。

【サンプルコード】

$.sleep = function(timeout) {
var d = new $.Deferred;
setTimeout(function(){
d.resolve("");
}, timeout);
return d.promise();
};

まとめ ~JavaScriptでsleepを実現する方法のポイント~

JavaScriptにはsleepが無いため自前で実装が必要ですが、他の言語と同じようにsleepを行うことができました。
最後にJavaScriptによるsleepの実現方法のポイントを整理いたします。

JavaScriptのsleepの実装方法のポイント

  • JavaScriptではsetTimeoutを利用してsleepを実現する
  • ジェネレータを使うことで直列処理でsleepを記述できる
  • Promiseを使うことでも直列処理でsleepを記述できる
  • async/awaitを使うことでよりシンプルにsleepを記述できる
  • ビジーwaitでsleepを実現する方法もあるがビジー状態になるため注意が必要である
  • jQueryを使う場合はDeferredオブジェクトでPromiseを使う場合と同じようにsleepを実現できる

Related