1. はじめに
JavaScriptで一定時間待ってから何か処理を行いたい場合、setTimeout() を使うこともできるのですが、読みにくいコードになってしまいます。そこで、Promise を使って読みやすくしてみます。
Promise というのは、非同期処理を分かりやすいコードで記述できるように用意されたオブジェクトです。ブラウザごとの対応状況は、Promises | Can I use… で確認できますが、最近だとIE以外のブラウザであれば対応しているようです。
追記
更に時は進み、ECMAScript 2017 (ECMA-262) という仕様で「async function」という機能が追加されたことにより、より簡単に Promise が使えるようになりました。今後はこちらの利用が推奨されると思いますので、こちらの例を先に紹介します。
ブラウザの実装状況:Async functions | Can I use…
分かりやすい説明ページ: The Modern Javascript Tutorial – Async/await
2. 手順(async function版)
まず、「指定された時間待つだけの関数を内包したPromiseオブジェクト」を返す関数を用意しておきます。
const wait = (sec) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, sec*1000);
//setTimeout(() => {reject(new Error("エラー!"))}, sec*1000);
});
};
(一定時間待つ処理のために、setTimeout を実行しています。)
あとは、この「一定時間待つ関数」と「その後で実行したい処理」を「async を付けた関数」の中に順番に記述するだけです。
async function main() {
try {
await wait(10); // ここで10秒間止まります
// ここに目的の処理を書きます。
} catch (err) {
console.error(err);
}
}
main();
(エラー処理のために、try...catch を使っています。)
この後の「Promise をそのまま使った」場合と比べると、断然こちらの方が読みやすいと思います。
async / await のポイントを書いておきます。
async
- 普通の関数の頭に
asyncを付けると、その関数はPromiseを返す関数になる。(Promiseオブジェクト以外を返す関数であっても、それをPromiseでラップして返すようになる) asyncを付けた関数内では、awaitが使えるようになる。asyncを付けた関数自体は同期処理にならない。
await
Promiseオブジェクトの頭につけると、そのPromiseオブジェクトが実行される。- その際、処理が終了するまで待って結果を受け取ることができる(同期処理になる)。
awaitが使えるのは、asyncを付けた関数の中だけである。
デモページを用意しました
3. 手順(Promise をそのまま使った版)
まず、「指定された時間待つだけの関数」を返す関数を用意しておきます。
var wait = function(sec) {
return function() {
return new Promise(function(resolve/*, reject*/) {
setTimeout(resolve, sec*1000)
});
}
};
分かりづらいですが、setTimeout を Promise で包んでいます。
あとはPromiseを使って、この関数の後に本来の処理を実行するだけです。
Promise.resolve()
.then(wait(10)) // ここで10秒待つ(「Promiseオブジェクトを返す関数」を thenに渡しています)
.then(function() {
// ここに目的の処理を書きます。
})
.catch(function (err) {
console.error(err);
self.result_message = error;
});
デモページを用意しました
4. いつ使うのか?
例えば、ユーザがボタンをクリックした時に重い同期処理を開始し、終了するまでの間、ローディングアイコンを表示したい場合に使えます。
先日、素数判定(リンク切れ)を作った時に使いました。というか、それがあったからこのページを書きました。
通常、ローディングアイコンを表示してそのまま重い同期処理を開始しようとすると、ローディングアイコンが表示される処理が始まる前に重い処理が始まってしまい画面のレンダリングがブロックされてしまってアイコンが表示されません。なのでローディングアイコンが表示されるのを待ってから、メインの処理を開始するようにします。




