JavaScriptのsetTimeoutでsleep処理をレベル1から5まで
どんなプログラミング言語であれSleepは良く使う処理の一つである。JavaScriptではsetTimeout関数を用いて行うが書き方は単純なものからPromiseを使うもの、Async/Awaitを使うものなど様々に書くことができる。
どの書き方が良いかというと、プロジェクトごとに使われる書き方を真似て書くのがコードの一貫性の面から正しい書き方であり、また可読性を考えるとチームの習熟度によっても何を使うべきかは分かれてくる。
ここではこの習熟度別にレベル1からレベル5まで書いていこうと思う。1~5秒待つ関数を作っていく。引数で任意の時間をまつ実装はまとめでちょろっと書くことにする。
レベル1:不採用と言わざるを得ない
JavaScript習いたての人が本を真似て書く記述である。ところがプロジェクトで今時こんなコードは化石と言われるレベルである。
var sleep1 = function() {
setTimeout(function() {
console.log('1000ms passed');
}, 1000);
};
sleep1();
レベル2:ECMA6覚えたて見習い
ECMA6の書き方を覚えた初心者が書くコードで少しこなれた感はあるが、記述がまだ冗長である。
// Arrow関数で簡潔に
// 変更しない関数はconstで宣言
const sleep2 = () => {
setTimeout(() => {
console.log('2000ms passed');
}, 2000);
};
sleep2();
レベル3:ワンライナーテクニシャン
1行で完結に書くことができる。習熟した者たちにとって1行当たりの情報量が増えると、画面当たりの情報量が増えて読みやすくなる。しかしレベル2以下のものが読むとかえって読みにくく感じる。
ただ後述するが、sleep後の処理が書けないという問題点がある。
const sleep3 = () => setTimeout(() => console.log('3000ms passed'), 3000);
sleep3();
レベル4:Promise使いの熟練JavaScripter
Promiseを使うとその後の処理をthenで繋いで簡潔に書くことができる。プロのJavaScripterが好んで使う技である。このsleep関数が書ければ私なら採用である。
// Promiseで待つ
const sleep4 = new Promise(resolve => setTimeout(resolve, 4000));
sleep4.then(() => console.log('4000ms passed'));
Promiseを使っていても下記のような冗長な書き方だと弱く見えてしまう。画面当たりの情報量を増やすために短く書けるならその方が良い。
var sleep4 = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 4000);
});
ただ、他のメンバーが上記のように書いていたらプロジェクト全体のコードを修正するか、合わせておくのが無難である。
レベル5:Async/Await使い
最新の文法を使用するチームならばAsync/Awaitを使っているだろう。まあsleep関数という意味ではレベル4の書き方が最もスマートであるが、無理やり使って書くと下記のようになる。Promiseの部分は同等である。
const sleep5 = async () => {
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('5000ms passed');
return 'That’s All';
};
sleep5().then(endMessage => console.log(endMessage));
結局sleep5はreturnで値を返している事を除けばsleep4の焼き直しに過ぎないが、sleep5関数内で複数のawaitを使うなど、より複雑な処理をする時は見通しが良くなる。
尚、constで定義せずに次のようにも書ける。ネットで調べるとこの関数定義の仕方ばかりでウンザリしている。
async function sleep5() {
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('5000ms passed');
};
sleep5();
まとめ・引数で指定された時間待つ書き方
最後に全コードと実行結果を載せておく。プロジェクトや状況に応じて様々なSleepを仕込めるようになろう。
// まず普通に作る
var sleep1 = function() {
setTimeout(function() {
console.log('1000ms passed');
}, 1000);
};
sleep1();
// Arrow関数で簡潔に
// 変更しない関数はconstで宣言
const sleep2 = () => {
setTimeout(() => {
console.log('2000ms passed');
}, 2000);
};
sleep2();
// 省略できる記述は省略する
const sleep3 = () => setTimeout(() => console.log('3000ms passed'), 3000);
sleep3();
// Promiseで待つ
const sleep4 = new Promise(resolve => setTimeout(resolve, 4000));
sleep4.then(() => console.log('4000ms passed'));
// async/awaitを使う
const sleep5 = async () => {
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('5000ms passed');
return 'That’s All';
};
sleep5().then(endMessage => console.log(endMessage));
実行結果
1000ms passed
2000ms passed
3000ms passed
4000ms passed
5000ms passed
That’s All
特定の時間待つならこれで良いが、引数で任意の時間を待ちたい場合、次のように書ける。
var sleep1 = function(ms) {
setTimeout(function() {
console.log(ms + 'ms passed');
}, ms);
};
sleep1(1000);
const sleep2 = (ms) => {
setTimeout(() => {
console.log(`${ms}ms passed`);
}, ms);
};
sleep2(2000);
const sleep3 = ms => setTimeout(() => console.log(`${ms}ms passed`), ms);
sleep3(3000);
const sleep4 = ms => new Promise(resolve => setTimeout(() => resolve(ms), ms));
sleep4(4000).then(ms => console.log(`${ms}ms passed`));
const sleep5 = async ms => {
await new Promise(resolve => setTimeout(() => resolve(ms), ms));
console.log(`${ms}ms passed`);
return 'That’s All';
};
sleep5(5000).then(endMessage => console.log(endMessage));
ではまた。