setTimeoutを用いて、非同期関数を同期的に動かす
JavaScriptやjQueryでプログラミングしていると、非同期関数を使用する箇所で、自分が意図しているのとは違う動きになることがあります。
その際、非同期関数を同期的に動かすことができれば、問題は解決します。
非同期関数(Asynchronous Function)
例えば、ボタンをクリックすることで、以下のことを順次実行したいとします。
- テキストエリアをスライドアップ
- テキストエリアに「10」と表示
- テキストエリアをスライドダウン
- テキストエリアに「20」と表示
単純にプログラムにすると、以下となります。
プログラム1
$(function(){ $('#btn').on('click', function() { $("#textarea").slideUp(1000); // 非同期関数 $("#textarea").html('10'); // 同期関数 $("#textarea").slideDown(1000); // 非同期関数 $("#textarea").html('20'); // 同期関数 }); });
上記プログラムの中で、slideUp() とslideDown() が、非同期関数です。
(注)slideUp(1000)
スライドアップ(slideUP)は、要素を上方向にスライドして隠します。
数字の1000は、デュレーション(duration)で、スライド開始から完了までの時間です。
単位はm秒で、1000と書けば、1000m秒すなわち1秒となります。
(注)slideDown(1000)
スライドダウン(slideDown)は、隠れている要素を下方向にスライドして表示します。
数字の1000は、デュレーション(duration)です。
非同期処理
JavaScriptでは、プログラム中に非同期関数があると、その関数の終了を待たず次に進みます(非同期処理)。
例えば、プログラム1 の3行目と4行目では、スライドアップのデュレーション(1秒)を待たず、次に進み、テキストエリアに「10」を表示します。
そのため、このプログラム1 を動かすと、以下の順番で動作します。
- テキストエリアに「10」を表示
- テキストエリアに「20」を表示
- テキストエリアをスライドアップ
- テキストエリアをスライドダウン
この動作は、当初、意図したのとは違う動きになっています。
プログラム2
後の説明のため、プログラム1 を変形させています。
プログラム1 とプログラム2 の動作は同じです。
<!DOCTYPE html> <html lang="jp"> <head> <meta charset="utf-8"> <title>Control Asynchronous Function Synchronously</title> <script src="../lib/jquery.min.js"></script> <script> var duration_A = 2000; var duration_C = 2000; $(function(){ $('#btn').on('click', function() { A(); // slideUp B(); // 10 C(); // slideDown D(); // 20 }); }); function A() { $("#textarea").slideUp(duration_A); } function B() { $("#textarea").html('10'); } function C() { $("#textarea").slideDown(duration_C); } function D() { $("#textarea").html('20'); } </script> </head> <body> <input type="button" id="btn" value="Click"><br> <textarea id="textarea" cols="50" rows="10" readonly></textarea> </body> </html>
プログラム2 の説明
グローバル変数
- duration_A: スライドアップのデュレーション。 2秒に設定。
- duration_C: スライドダウンのデュレーション。 2秒に設定。
setTimeoutを用いた同期処理
考え方
非同期処理
非同期関数A()の後に、同期関数B()があった場合、先にB()が動き、次にA()が動きます。
A(); // 非同期関数 B(); // 同期関数
- 同期関数B()を実行
- 非同期関数A()を実行
同期処理
非同期関数A()の処理時間はduration_Aなので、同期関数B()をduration_A後に動くようにします。
A(); // 非同期関数 setTimeout( function() { B(); // 同期関数 }, duration_A);
setTimeoutを用いることで、duration_A後にB() が動きます。
- 非同期関数A()を実行
- duration_A後にB()を実行
プログラム3
プログラム2 の13-16行目を書き換えました。
<!DOCTYPE html> <html lang="jp"> <head> <meta charset="utf-8"> <title>Control Asynchronous Function Synchronously</title> <script src="../lib/jquery.min.js"></script> <script> var duration_A = 2000; var duration_C = 2000; $(function(){ $('#btn').on('click', function() { A(); // Asynchro. Func. setTimeout(function() { B(); // Synchro. Func. C(); // Asynchro. Func. setTimeout(function() { D(); // Synchro. Func. }, duration_C); }, duration_A); }); }); function A() { $("#textarea").slideUp(duration_A); } function B() { $("#textarea").html('10'); } function C() { $("#textarea").slideDown(duration_C); } function D() { $("#textarea").html('20'); } </script> </head> <body> <input type="button" id="btn" value="Click"><br> <textarea id="textarea" cols="50" rows="10" readonly></textarea> </body> </html>
プログラム2 の説明
プログラム2 の、13行目~20行目を抜き出しました。 ここは、ボタンをクリックした後の処理です。
$('#btn').on('click', function() { A(); // Asynchro. Func. setTimeout(function() { B(); // Synchro. Func. C(); // Asynchro. Func. setTimeout(function() { D(); // Synchro. Func. }, duration_C); }, duration_A); });
以下の順で、同期的にプログラムが動きます。
- A()が動き始め、2秒後に動き終わる
- B()が動く
- C()が動き始め、2秒後に動き終わる
- D()が動く
(注意)duration_A, duration_C の値は、共に2秒です。