jQuery.Deferred学習メモ(5)jQuery.when()で複数PromiseのAND条件を判定

2013-09-16

旧ブログ

t f B! P L

前の関連記事:jQuery.Deferred学習メモ(4)$.ajax()はPromiseを含むjqXHRオブジェクトを返す


jQuery.when()で複数PromiseのAND条件判定ができます。複数Promiseが全てfulfilledになれば$.when()のPromiseもfulfilledになり、各引数もそれぞれ付随します。

$.when()のPromiseは複数の引数を付随できる


$.when()は複数のPromiseのAND条件判定ができます。

テストコード⑥で導入したsetTimeoutで1000ミリ後に結果を表示するPromiseと、テストコード⑨で導入した.animate()で1500ミリ秒続くアニメーションを表示するPromiseテストコード⑭で導入した$.getJSONでブログの更新日時を得るPromiseを$.when()でAND条件で繋ぐテストコード⑮を作ります。

テストコード⑮
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<button id="btn1">未完</button><button id="btn2">完了</button><button id="btn3">失敗</button><br />
<div id="df_ex">準備中<br/></div>
<script>
var df1 = $.Deferred();
function fn1(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  }
function fn2(n) {
  var df2=$.Deferred();
  $.when(  
     $( "#df_ex" ).animate(
       {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"},
       {duration:1500, easing: "linear"}
       ),
     $.getJSON("http://p--q.blogspot.jp/feeds/posts/default?alt=json-in-script&callback=?"
       ).done(function(json){
         $( "#df_ex" ).append("ブログ最終更新日時: "+json.feed.updated.$t+"</br>");
        }),
     fn3(n)
    ).done(function(a1,a2,a3){df2.resolve(a3)});
  return df2
  }
function fn3(n) {
  var df3=$.Deferred();
  var n3;
  setTimeout(function(){
    fn1(n);
    n3="で.alwaysも実行";
    df3.resolve(n3);
    }, 1000);
  return df3;
  }
df1
 .then(fn2,fn2,fn1)
 .always(fn1)
$( "button#btn1" ).on( "click", function() {
  df1.notify("で.progress実行");
});
$( "button#btn2" ).on( "click", function() {
  df1.resolve("で.done実行");
});
$( "button#btn3" ).on( "click", function() {
  df1.reject("で.fail実行");
});
</script>
アニメーションの効果を最初から確認できるように③行目で最初から「準備中」という文字を表示させています。

テストコード⑥で導入したsetTimeoutで1000ミリ後に結果を表示するPromiseの部分は関数fn3にくくり出しています。

さらに$.when()の複数の引数の例のためにfn3はわざわざ3つのPromiseの最後にもってきています。

$.when()の引数になっている3つのPromiseが全てfulfilledになると$.when()のPromiseもfulfilledになり、21行目の.done()が実行されて$.when()の引数の3つ目のPromiseの引数a3がdf.2.resolve()の引数として渡されます。

これでfn2のPromiseがfulfilledになるので、36行目の保留にされていた.always()が引数a3を受け取って実行されます。

「完了」ボタンをクリックすると12行目の.animate()で「準備中」の文字がアニメーションで拡大されていきます。

1000ミリ秒後に20行目のfn3から27行目のsetTimeout()で「resolvedで.done実行」が表示されます。


次に16行目の$.getJSONでブログ最終更新日時が表示されましたが、ネットの接続速度によっては「resolvedで.done実行」より先に表示されるかもしれません。

1500ミリ秒後にアニメーションも終了すると$.when()の引数になっている3つのPromiseが全てfulfilledになり、$.when()のPromiseもfulfilledになり、21行目の.done()が実行されてdf2がresolveされて、36行目の.alwaysが実行されます。


$.when()自身もPromiseを持っているのでこれをreturnすれば、わざわざjQuery.Deferredオブジェクトのdf2を作成しなくても同じことが実現できます。

テストコード⑮を$.when()をreturnする方法に改造します。

テストコード⑯
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<button id="btn1">未完</button><button id="btn2">完了</button><button id="btn3">失敗</button><br />
<div id="df_ex">準備中<br/></div>
<script>
var df1 = $.Deferred();
function fn1(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  }
function fn2(n) {
    return $.when( 
      fn3(n),
      $( "#df_ex" ).animate(
         {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"},
         {duration:1500, easing: "linear"}
        ),
      $.getJSON("http://p--q.blogspot.jp/feeds/posts/default?alt=json-in-script&callback=?"
         ).done(function(json){
         $( "#df_ex" ).append("ブログ最終更新日時: "+json.feed.updated.$t+"</br>");
           })
        )
  }
function fn3(n) {
        var df3=$.Deferred();
        var n3;
        setTimeout(function(){
          fn1(n);
          n3="で.alwaysも実行";
          df3.resolve(n3);
         }, 1000);
         return df3;
       }
df1
  .then(fn2,fn2,fn1)
  .always(fn1)
$( "button#btn1" ).on( "click", function() {
  df1.notify("で.progress実行");
});
$( "button#btn2" ).on( "click", function() {
  df1.resolve("で.done実行");
});
$( "button#btn3" ).on( "click", function() {
  df1.reject("で.fail実行");
});
</script>
$.when()の中の引数になっている3つのPromiseの付随する引数のうちfn3のものを使うので先頭の11行目にもってきています。

引数を1個だけしか利用しない場合はこうすることでとくに指定しなくても34行目の.always()の引数になります。

$.when()をreturnすればPromise animationや$.ajax()のPromiseにも引数を設定できる


$.when()を使えばPromise animationや$.ajax()のreturnしたときにこれらのPromiseに新たに引数を設定されたかのような動作をさせることができることに気がつきました。

jQuery.Deferred学習メモ(3)deferred.then()でPromise animationをreturnするのテストコード⑨を$.when()を使って.then()の次に引数を渡しすようにします。

テストコード⑰
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<button id="btn1">未完</button><button id="btn2">完了</button><button id="btn3">失敗</button><br />
<div id="df_ex"></div>
<script>
var df1 = $.Deferred();
function fn1(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  }
function fn2(n) {
  var n2="で.alwaysも実行";
  fn1(n);
  return  $.when(
             fn4(n2),
             $( "#df_ex" ).animate(
               {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"},
               {duration:1500, easing: "linear"}
               ))
  }
function fn4(n2){
    var df4=$.Deferred();
    df4.resolve(n2)
    return df4;
  }
df1
 .then(fn2,fn2,fn1)
 .always(fn1)
$( "button#btn1" ).on( "click", function() {
  df1.notify("で.progress実行");
});
$( "button#btn2" ).on( "click", function() {
  df1.resolve("で.done実行");
});
$( "button#btn3" ).on( "click", function() {
  df1.reject("で.fail実行");
});
</script>
Promiseに引数を設定できない.animate()を$.when()で引数をPromiseの引数で返す13行目のfn4関数を先頭にもってくることでPromiseに引数n2をつけて26行目の.always()に渡しています。

$.when()の条件ではPromiseを返さない場合はPrpmiseがfulfilledと解されるそうなのでわざわざDeferredオブジェクトを作らずに引数をreturnするだけでも同じことができました。

jQuery.Deferred学習メモ(4)$.ajax()はPromiseを含むjqXHRオブジェクトを返すのテストコード⑭はDeferredオブジェクトを使わずにPromiseに引数を設定します。

テストコード⑱
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<button id="btn1">未完</button><button id="btn2">完了</button><button id="btn3">失敗</button><br />
<div id="df_ex"></div>
<script>
var df1 = $.Deferred();
function fn1(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  }
function fn2(n) {
    var n2="で.alwaysも実行";
    fn1(n);
    return $.when(
             fn4(n2),
             $.getJSON("http://p--q.blogspot.jp/feeds/posts/default?alt=json-in-script&callback=?"
               ).done(function(json){
                   $( "#df_ex" ).append("ブログ最終更新日時: "+json.feed.updated.$t+"</br>");
                  })
               )
  }
function fn4(n2){
    return n2;
  }
df1
 .then(fn2,fn2,fn1)
 .always(fn1)
$( "button#btn1" ).on( "click", function() {
  df1.notify("で.progress実行");
});
$( "button#btn2" ).on( "click", function() {
  df1.resolve("で.done実行");
});
$( "button#btn3" ).on( "click", function() {
  df1.reject("で.fail実行");
});
</script>
以上でjQueryのPromiseの基本的なことは網羅したと思います。

SyntaxHighlighter+fleXcrollの高速化が目的なので網羅する必要はなかったのですが、jQuery.DeferredとPromiseの関係がいまいち掴めなかったので結局jQueryサイトでPromise検索してでてくるAPIの解説を片っ端から読むしかありませんでした。

jQueryの変遷もわかって、開発者たちの苦労が伝わってきてまあ面白いですけどね。

日常的にプログラミングをしているわけではないので、忘れてしまっても理解できるように些細なことも繰り返し書くようにしました。自分自身の覚書ですので。

オブジェクトとか、メソッドとか、クラスとか用語の使い分けがまだいまいちわかりませんのでおかしな記載もたくさんあると思いますが、テストコードは全てBloggerの投稿画面のHTMLモードに貼り付けて動作確認しています。

Promiseにくっついてくる値の呼び方をどうするかに悩みました。

解説ではargumentとなっているので引数でよいのでしょうけど、そとから見る限りはPromiseは関数にはみえないですし、変数とかプロパティならそれはunfulfilledとかfulfilledがそれに該当しそうです。

モバイルWimaxはブチブチ切断しますし、JavaScriptの変数のウォッチの方法もまだわからないので恐ろしく効率が悪いです。

ひとつのテストコードをいじくり倒してBloggerの投稿だけで動くような例を作るようにしました。

これでようやくSyntaxHighlighter+fleXcrollの高速化に取り掛かれます。

でも、SyntaxHighlighterの表示が遅い最大の原因はBlogger:GTmetrixのWaterfallをみてページ速度向上を考えるで外部ファイルの読み込みにあることに気づきました。

これも解決方法を思いついたので早く実践したいです。

もう表示が遅くてプレビューをみて画面を確認するのがいらいらします。

いま台風が来ていて天気が悪いせいかモバイルWimaxの電波がブチブチブチブチ切れて何回も同じボタンを押さないといけないのもとっても苦痛です。ここが田舎というせいではありますけど。

参考にしたサイト


jQuery.when() | jQuery API Documentation
本家の.when()の解説。

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ