jQuery.Deferred学習メモ(3)deferred.then()でPromise animationをreturnする

ラベル:

前の関連記事:jQuery.Deferred学習メモ(2)jQuery1.8以降のdeferred.then()を使う


jQuery.Deferredオブジェクト以外でもPromiseを返すメソッドがあります。それらを使うとjQuery.Deferredオブジェクトを作らなくてもdeferred.then()でPromiseを返せます。まずはアニメーション関係のメソッドです。

.animate()はPromiseを返す。ただしPromiseに付随する引数は返せない。


jQuery.Deferred学習メモ(2)jQuery1.8以降のdeferred.then()を使うの最後にでてきたテストコード⑦を改造してsetTimeoutの変わりに.animate()を使って次の.always()を待たせてみます。

まずは普通にjQuery.Deferredオブジェクトを新たに作って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"></div>
<script>
var df1 = $.Deferred();
function fn1(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  }
function fn2(n) {
  var df2=$.Deferred();
  var n2;
  fn1(n);
  $( "#df_ex" ).animate(
      {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"}, 
      {duration:1500, easing: "linear",
       complete: function(){n2="で.alwaysも実行"; df2.resolve(n2);}
      } 
    );
  return df2;
  }
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>
fn2の中で10行目で新たにjQeury.Deferredオブジェクトdf2を作っています。

テストコード⑦ではsetTimeoutの中でfn1(n)を実行しfn1の表示を1秒待たせていましたが、テストコード⑧ではまずfn1を表示させてそれを1.5秒かけて拡大表示のアニメーションにしています。

17行目でdf2をreturnして次の.always()の実行を待たせています。

アニメーション終了後の16行目のdf2.resolve(n2)で待たせているのを再開します。

「完了」ボタンをクリックしてみると、まず22行目の.then()のdoneFilterが実行されて9行目のfn2が呼ばれて12行目のfn1(n)が実行されて、「resolvedで.done実行」が表示されます。


続いて13行目の.animate()で1.5秒かけて字が大きくなって、その後に23行目の.alwaysが実行されて「resolvedで.alwaysも実行」が表示されます。


テストコード⑧では19行目でjQuery.Deferredオブジェクトのdf2をreturnしてunfulfilledのPromise返しましたが、今度は.animate()の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) {
  fn1(n);
  return  $( "#df_ex" ).animate(
      {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"}, 
      {duration:1500, easing: "linear"}
    );
  }
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>
11行目で.animate()メソッドをつけたjQueryオブジェクトをreturnしています。

「完了」ボタンをクリックしてみるとテストコード⑧と同様な動作をします。

これは.animate()の中でjQuery.deferredオブジェクトが作られて、それをresolveするということが行われているからです。

ですのでテストコード⑧の10行目と16行目に相当するコードは書かなくてもよいわけです。


でも、ちょっと表示されているものが違います?

実は、.then()ではPromiseの引数を返さないといけないのを返していないのです。

テストコード⑦では「で.alwaysも実行」となっていたところが「[object Object]」となっているのはそのためです。

undefinedではなく、[object Object]となっているのはなんらかのオブジェクトを設定すれば解決しそうにみえますがいまのところよくわかりません。

普通は「return 引数」か「.resolve(引数)」で返すのですが、Promise animationに引数をつける方法がわからないので引数を返していません。

http://code.jquery.com/jquery-1.9.1.jsにアクセスしてjQeuryのソースを確認してみると、やはり.animation()メソッドのなかでローカルにPromiseを作って、中でresolveしてしまっているので引数を外からくっつけるのは難しそうです。

変更した引数を返すためにはテストコード⑧のやり方をするしかないようです。

この問題は$.when()を使って解決できました。

jQuery.Deferred学習メモ(5)jQuery.when()で複数PromiseのAND条件を判定のテストコード⑰へ。

引数をそのまま次へ受け渡すときは.then()でなく.done()を使えばいいでしょう。

今度はテストコード⑨で.animate()メソッドをつけたjQueryオブジェクトではなく引数を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"></div>
<script>
var df1 = $.Deferred();
function fn1(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  }
function fn2(n) {
  fn1(n);
  $( "#df_ex" ).animate(
      {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"}, 
      {duration:1500, easing: "linear"}
    );
  return "で.alwaysも実行"; 
  }
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>
15行目で単に引数だけreturnしています。


Promiseの引数はちゃんと受け継がれていますが、.then()の次の.alwaysの実行は待ってくれず、「resolvedで.alwaysも実行」が表示されてからアニメーションが始まります。

Promise animationを返すメソッド


.animate()以外にもアニメーションのメソッドはPromiseを返します。

jQuery API DocumentationでPromise animationを検索するとたくさんでてきます。

.fadeIn() .fadeOut() .fadeToggle() .hide().show() .slideDown() .slideToggle() .slideUp() .toggle()がでてきます。

Promise animationのPromiseは.promise()で引き出して.done()できる


テストコード⑨ではPromise animationのunfulfiledのPromiseを.then()にreturnして次のコードの実行を保留にしましたが、Promise animationのPromiseを.promise()メソッドで取り出すことも可能です。

テストコード⑧の16行目を書き換えて.animate()のPromiseを.promise()で取り出して.done()で受けて同じ処理をさせてみます。

テストコード⑪
<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 df2=$.Deferred();
  var n2;
  fn1(n);
  $( "#df_ex" ).animate(
      {width: "70%", opacity: 0.4, marginLeft: "0.6in", fontSize: "3em"},
      {duration:1500, easing: "linear"}
    ).promise().done(function(){n2="で.alwaysも実行"; df2.resolve(n2);});
  return df2;
  }
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>
returnでPromise animationを返すとPromiseの状態に従って対応してくれるので、.animate().done()でいけそうに思いますが、これでは動かなくて.animate().promise().done()というように.promise()を間に挟まないといけないです。

これは.animate()の中で作られたPromiseがfulfilledになっときに.done()するということです。

.done()の他に.then(), .fail(), .always(), .progress(), .state()も使えるようです。

Promiseを理解したあとならテストコード⑧の書き方よりこのテストコード⑪の書き方の方がわかりやすいかもしれませんね。

参考にしたサイト


animate(params, options) - jQuery 日本語リファレンス
.animate()の第2引数以降をオプションで指定するやり方の解説。

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

.animate() | 私的なjQuery
日本語で詳しく.animate()が解説されています。

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

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

PR

0 件のコメント:

コメントを投稿