Symfoware

Symfowareについての考察blog

jQuery.DeferredでJavaScriptの非同期処理を綺麗に書く

Cordovaのチュートリアルをやっている時に気がついたのですが、

service.initialize().done

「done」って何なんだろう?と思い、調べてみました。


jQuery.Deferred



こちらが非常にわかりやすかったです。
爆速でわかるjQuery.Deferred超入門

非同期処理中の状態を保持するオブジェクトを作成。
成功の可否により、処理を分岐できるようです。



基本的な使い方



参考サイトに記載されている処理そのままですが、実際に動かしてみます。


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8">
  5.     <meta http-equiv="Content-Script-Type" content="text/javascript">
  6.     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  7.     <title>jQuery.Deferred</title>
  8. </head>
  9. <body>
  10. <script language="JavaScript" type="text/javascript">
  11. $(function(){
  12.     
  13.     //1秒後にHello!を出力するDeferred対応関数。必ずresolveする
  14.     function delayHello() {
  15.         var d = new $.Deferred;
  16.         setTimeout(function(){
  17.             $('#info').append('<div>Hello!</div>');
  18.             d.resolve();
  19.         }, 1000);
  20.         return d.promise();
  21.     }
  22.     //1秒後にエラーを発生させるDeferred対応関数。必ずrejectする
  23.     function delayError() {
  24.         var d = new $.Deferred;
  25.         setTimeout(function(){
  26.             d.reject('Error!!');
  27.         }, 1000);
  28.         return d.promise();
  29.     }
  30.     
  31.     function hello1() {
  32.         $('#info').append('<div>Hello sync1</div>');
  33.     }
  34.     function hello2(e) {
  35.         $('#info').append('<div>' + e + '</div>');
  36.         $('#info').append('<div>Hello sync2</div>');
  37.     }
  38.     
  39.     // resolveされたら最初の引数
  40.     // rejectされたら、二番目の引数の関数が実行される
  41.     
  42.     $('#delayHello').on('click', function() {
  43.         //delayHello()が返すDeferredは1秒後にresolvedになる。
  44.         //それなので、1秒後にhello1だけが実行される
  45.         delayHello()
  46.         .then(hello1, hello2);
  47.     });
  48.     
  49.     $('#delayError').on('click', function() {
  50.         //delayError()が返すDeferredは1秒後にrejectedになる。
  51.         //それなので、1秒後にhello2だけが実行される
  52.         delayError()
  53.         .then(hello1, hello2);
  54.     });
  55.     
  56. });
  57. </script>
  58. <button id="delayHello">delayHello</button><button id="delayError">delayError</button>
  59. <div id="info"></div>
  60. </body>
  61. </html>




※動くサンプルはこちらに置いておきます。
http://symfo.web.fc2.com/js-sample/deferred/sample1.html


delayHelloを実行すると、一秒後にhello1が実行されます。

568_01.png


delayErrorを実行すると、一秒後にhello2が実行されます。

568_02.png


重要なのは、「Hello!」が表示された後にhello1の実行結果「Hello sync1」が
表示されることです。

これで非同期なタイマー処理の結果を待つことが出来ました。





doneとfail



then(成功, 失敗)

のように同時に定義することも、doneとfailを使って別々に
定義することもできるようです。
Cordovaのチュートリアルに登場した「done」はこれですね。

ちょっとサンプルを書き換えてみます。


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8">
  5.     <meta http-equiv="Content-Script-Type" content="text/javascript">
  6.     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  7.     <title>jQuery.Deferred 2</title>
  8. </head>
  9. <body>
  10. <script language="JavaScript" type="text/javascript">
  11. $(function(){
  12.     
  13.     //1秒後にHello!を出力するDeferred対応関数。必ずresolveする
  14.     function delayHello() {
  15.         var d = new $.Deferred;
  16.         setTimeout(function(){
  17.             $('#info').append('<div>Hello!</div>');
  18.             d.resolve();
  19.         }, 1000);
  20.         return d.promise();
  21.     }
  22.     //1秒後にエラーを発生させるDeferred対応関数。必ずrejectする
  23.     function delayError() {
  24.         var d = new $.Deferred;
  25.         setTimeout(function(){
  26.             d.reject('Error!!');
  27.         }, 1000);
  28.         return d.promise();
  29.     }
  30.     
  31.     function hello1() {
  32.         $('#info').append('<div>Hello sync1</div>');
  33.     }
  34.     function hello2(e) {
  35.         $('#info').append('<div>' + e + '</div>');
  36.         $('#info').append('<div>Hello sync2</div>');
  37.     }
  38.     
  39.     // resolveされたらdone
  40.     // rejectされたらfailが実行される
  41.     
  42.     $('#delayHello').on('click', function() {
  43.         //一旦promiseを受けっとった後分岐
  44.         var promise = delayHello();
  45.         promise.done(hello1);
  46.         promise.fail(hello2);
  47.     });
  48.     
  49.     $('#delayError').on('click', function() {
  50.         var promise = delayError();
  51.         promise.done(hello1);
  52.         promise.fail(hello2);
  53.     });
  54.     
  55. });
  56. </script>
  57. <button id="delayHello">delayHello</button><button id="delayError">delayError</button>
  58. <div id="info"></div>
  59. </body>
  60. </html>




処理結果は同様です。
だんだん使い方がわかってきました。

サンプルはこちらに置いておきます。
http://symfo.web.fc2.com/js-sample/deferred/sample2.html



.then()による連結



ここから更に面白くなってきます。
thenの最初の引数は成功時、第二引数は失敗時に実行されるのでした。

thenをチェーンして実行してみます。


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8">
  5.     <meta http-equiv="Content-Script-Type" content="text/javascript">
  6.     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  7.     <title>jQuery.Deferred 3</title>
  8. </head>
  9. <body>
  10. <script language="JavaScript" type="text/javascript">
  11. $(function(){
  12.     
  13.     //1秒後にHello!を出力するDeferred対応関数。必ずresolveする
  14.     function delayHello() {
  15.         var d = new $.Deferred;
  16.         setTimeout(function(){
  17.             $('#info').append('<div>Hello!</div>');
  18.             d.resolve();
  19.         }, 1000);
  20.         return d.promise();
  21.     }
  22.     //1秒後にエラーを発生させるDeferred対応関数。必ずrejectする
  23.     function delayError() {
  24.         var d = new $.Deferred;
  25.         setTimeout(function(){
  26.             d.reject('Error!!');
  27.         }, 1000);
  28.         return d.promise();
  29.     }
  30.     
  31.     function hello1() {
  32.         $('#info').append('<div>Hello sync1</div>');
  33.     }
  34.     function hello2(e) {
  35.         $('#info').append('<div>' + e + '</div>');
  36.         $('#info').append('<div>Hello sync2</div>');
  37.     }
  38.     
  39.     $('#delayHello').on('click', function() {
  40.         delayHello()
  41.         .then(hello1)
  42.         .then(hello1)
  43.         .then(hello1);
  44.     });
  45.     
  46.     $('#delayError').on('click', function() {
  47.         delayError()
  48.         .then(hello1, hello2)
  49.         .then(hello1)
  50.         .then(hello1, hello2);
  51.     });
  52.     
  53. });
  54. </script>
  55. <button id="delayHello">delayHello</button><button id="delayError">delayError</button>
  56. <div id="info"></div>
  57. </body>
  58. </html>




delayHelloを実行すると、Hello sync1が3つ表示されます。

568_03.png


delayErrorは、後段にもエラーが伝播するようです。
ただし、新しいPromiseオブジェクトが生成されるので、エラーの内容は失われます。

568_04.png



新しいpromiseオブジェクトを生成することで、エラーから回復することができます。


  1.     $('#delayError').on('click', function() {
  2.         delayError()
  3.         .then(hello1, hello2)
  4.         .then(hello1,
  5.             function(e){
  6.                 $('#info').append('<div>エラーから回復しました</div>');
  7.                 return new $.Deferred().resolve().promise();
  8.             }
  9.         )
  10.         .then(hello1, hello2);
  11.     });




568_05.png

実際にはthenとfailと組み合わせて使うことになりそうです。
try catch風の処理が実装できます。


  1. delayError()
  2. .then(hello1)
  3. .then(hello1)
  4. .fail(function(e){
  5. console.log(e);
  6. console.log('エラーを処理しました');
  7. });



このサンプルはここに置いておきます。
http://symfo.web.fc2.com/js-sample/deferred/sample3.html




$.when()による並列連結



$.whenを使用すると、全ての引数が成功 or 1つでも失敗で分岐できます。


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8">
  5.     <meta http-equiv="Content-Script-Type" content="text/javascript">
  6.     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  7.     <title>jQuery.Deferred 4</title>
  8. </head>
  9. <body>
  10. <script language="JavaScript" type="text/javascript">
  11. $(function(){
  12.     
  13.     //1秒後にHello!を出力するDeferred対応関数。必ずresolveする
  14.     function delayHello() {
  15.         var d = new $.Deferred;
  16.         setTimeout(function(){
  17.             $('#info').append('<div>Hello!</div>');
  18.             d.resolve();
  19.         }, 1000);
  20.         return d.promise();
  21.     }
  22.     //1秒後にエラーを発生させるDeferred対応関数。必ずrejectする
  23.     function delayError() {
  24.         var d = new $.Deferred;
  25.         setTimeout(function(){
  26.             d.reject('Error!!');
  27.         }, 1000);
  28.         return d.promise();
  29.     }
  30.     
  31.     function hello1() {
  32.         $('#info').append('<div>Hello sync1</div>');
  33.     }
  34.     function hello2(e) {
  35.         $('#info').append('<div>' + e + '</div>');
  36.         $('#info').append('<div>Hello sync2</div>');
  37.     }
  38.     
  39.     $('#delayHello').on('click', function() {
  40.         // 全て成功
  41.         $.when(delayHello(), delayHello(), delayHello())
  42.         .then(hello1, hello2);
  43.     });
  44.     
  45.     $('#delayError').on('click', function() {
  46.         // 一つ失敗
  47.         $.when(delayHello(), delayHello(), delayError())
  48.         .then(hello1, hello2);
  49.     });
  50.     
  51. });
  52. </script>
  53. <button id="delayHello">delayHello</button><button id="delayError">delayError</button>
  54. <div id="info"></div>
  55. </body>
  56. </html>



delayHello

568_06.png


delayError

568_07.png


サンプルはここに置いておきます。
http://symfo.web.fc2.com/js-sample/deferred/sample4.html



めっちゃ便利な機能じゃないですか。
今まで知らなかったのが悔やまれます。
関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2015/02/24(火) 21:42:47|
  2. 備忘録
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<jQueryのajax通信で「No 'Access-Control-Allow-Origin' header」 | ホーム | Cordova コマンドのチートシート>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1655-ad036fd2
この記事にトラックバックする(FC2ブログユーザー)