JavaScriptの非同期処理
同期処理と非同期処理
時間の掛かる処理があります。例えば、インターネット通信や、ファイルの読み込みといった類の処理です。これらの処理を一般的な方法で実装しようとすると、その処理を行う間、ユーザーからの入力が受けられない状態になってしまいます。これが俗に言う、フリーズした
状態です。
幸いなことに、JavaScript では、時間の掛かる処理の待ち時間を、他の処理のために充てることができるような仕組みが用意されています。これを、非同期処理といいます。JavaScript における非同期処理は、歴史的にはコールバックを用いて処理されます。
setTimeout(() => {
console.log("Text 1");
}, 1000);
console.log("Text 2");
setTimeout
関数は、2 つの引数をとり、第 1 引数で指定された関数を、第 2 引数で指定された時間(ミリ秒)後に実行します。このように、システムによって処理の完了後に呼び出される関数を、コールバック関数と呼びます。上記の例では、コールバック関数の実行は後回しにされ、非同期的に実行されます。このため、実行結果はText 2
、Text 1
の順となります。
それでは、setTimeout
関数を用いて、1 秒毎に異なるメッセージを表示させることを考えてみましょう。すぐに思いつくのは、以下のようなコードです。
setTimeout(() => {
document.write("Text 1");
setTimeout(() => {
document.write("Text 2");
setTimeout(() => {
document.write("Text 3");
setTimeout(() => {
document.write("Text 4");
}, 1000);
}, 1000);
}, 1000);
}, 1000);
くどいですね。一昔前の JavaScript では、上記コードのように、コールバック関数が大量に使用された結果、インデントが非常に深くなるという事態に陥っていました(コールバック地獄)。
この状況を解決するために生まれたのが、Promiseと呼ばれる考え方です。Promise を用いると、上記のコードは以下のように書き換えることができます。
function mySetTimeout(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
mySetTimeout(1000)
.then((value) => {
document.write("Text 1");
return mySetTimeout(1000);
})
.then((value) => {
document.write("Text 2");
return mySetTimeout(1000);
})
.then((value) => {
document.write("Text 3");
return mySetTimeout(1000);
})
.then((value) => {
document.write("Text 4");
return mySetTimeout(1000);
});
Promise に対応する関数やメソッドの作成
setTimeout
関数は、昔ながらのコールバック関数を用いた形式となっています。そのため、Promise を用いてsetTimeout
を利用するためには、setTimeout
を Promise に対応させるための関数(ラッパー関数)を作成する必要があります。
Promise に対応する非同期処理関数は、Promise クラスのインスタンスを返します。Promise クラスのコンストラクタは、ただ 1 つの引数をとります。この引数はシステムによって呼び出される関数で、呼び出し可能な二つの引数(resolve
とreject
)をとることができます。非同期処理はその関数の中で実行し、正常に終了した時点でresolve
を、異常終了した場合はreject
を呼びます。
resolve
はただ一つの引数を取り、非同期処理の結果を渡します。reject
はただ一つの引数を取り、処理の失敗の理由を渡します。