jQuery.Deferred学習メモ(2)jQuery1.8以降のdeferred.then()を使う

ラベル:

前の関連記事:jQuery.Deferred学習メモ(1)$.DeferredでPromiseを操作する


deferred.then()はjQuery1.8で引数を新たに追加する従来のdeferred.pipe()に取って代わりました。テストコードでどうかわったのかをみてみます。

deferred.then()はPromiseに付随する引数を新しくする


jQuery.Deferred学習メモ(1)$.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();
df1
  .progress(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  })
  .done(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  })
  .fail(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  })
  .always(function() {
    $( "#df_ex" ).append(df1.state()+"で.always実行"+"<br/>");
  });
$( "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>
16行目の.always()はPromiseがfulfilledであってもfailedであっても実行されますが、引数を設定していません。

これに引数nを設定して実行する内容を.done()などと同じコードにしてみます。
  .always(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  });
「完了」ボタンをクリックすると.always()で実行したのに.done()で実行したのと同じ内容が表示されます。


こういうときにjQuery1.8以降の.then()を使えば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();
df1
  .progress(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  })
  .then(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
    return "で.alwaysも実行" ;
  })
  .fail(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  })
  .always(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  });
$( "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>
12行目の.then()の最後にreturn "で.alwaysも実行" ;として新たな引数を設定しています。

「完了」ボタンをクリックすると.always()が受け取る引数は.done()が受け取るものと異なり、新たな引数を受け取っていることがわかります。


1行目のjQueryのバージョンを1.9.1から1.7.1に変更してみるとどうなるでしょう?


.always()が受け取る引数は.done()と同じままです。

jQuery1.8以前の.then()は単に.done()、.fail()、.pregress()を繋げただけのものですが、jQuery1.8からはPromiseの引数を新しくします。

これはjQuery1.8以前の.pipe()と同じ働きになります。

.pipe()は.jQuery1.8以降では廃止になるそうです。

deferred.then()は引数を返さないとPromiseの引数はundefinedになってしまう


テストコード②で12行目のreturn "で.aliwaysも実行";を削除して.then()で引数を返さないようにします。


.always()で受け取った引数はundefinedになってしまっています。

つまり新しく作られた引数は中身がないということです。

このため.then()を使ったメソッドの後に引数を使ったメソッドがある場合は忘れずにreturnで引数を返しておかなくてはいけません。

deferred.then()を使って.done()、.fail()、.pregress()をまとめる


.then()は.then(fulfilledのときの処理, failedのときの処理, unfulfilledのときの処理)というように.done()、.fail()、.pregress()をまとめることができますのでそうしてみます。

テストコード③
<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();
df1
  .then(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
    return "で.alwaysも実行" ;
  },function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
    return "で.alwaysも実行" ;
  },function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  })
  .always(function(n) {
    $( "#df_ex" ).append(df1.state()+n+"<br/>");
  });
$( "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は引数に関数も使えるので同じコードは関数でまとめてしまうとすっきりします。

.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/>");
    return "で.alwaysも実行" ;
  }
df1
  .then(fn1,fn1,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がfailedのときは何もしないときなどは.then(fn1, null, fn1)というように該当部分をnullにします。

Promiseがunfulfilledのときは.always()は呼び出されないのでそのときは8行目の return "で.aliwaysも実行" ;は不要です。

また.always()のときも引数を返しても受けてとるメソッドがないので不要です。

このままでも動作に支障はありませんが、不要な命令を実行させるのは気持ち悪いので次のようにしました。

テストコード⑤
<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 "で.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>
次のメソッドにPromiseに付随する引数を渡すときはfn2を使っています。

deferred.then()はPromiseをリセットしたかのような動作をする


jQuery1.8以降の.then()の中ではPromiseに付随する引数が新たに作成され、return "引数";でその引数を設定できることがわかりました。

このときPromiseの状態は変化しないため、すぐに次のメソッドが実行されてしまいます。

ところがreturnで新たに作ったDeferredオブジェクトを返すとPromiseをunfulfilledにリセットしたかのような動作をさせることができます。

これを使えば.then()の次のメソッドに処理の結果がでてから引数を渡すことができます。

まず引数だけを返した場合は処理を待ってくれないことを確認します。

テストコード⑤でfn2で1000ミリ秒経ってから、fn1を呼び出し、新しい引数n2を返すようにします。

テストコード⑥
<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;
    setTimeout(function(){
      fn1(n);
      n2="で.alwaysも実行";
      }, 1000);
     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>
これで「完了」ボタンをクリックしてみます。


これは.then()のfn2の結果を待たずに.always()が先に実行された結果です。

最初が.always()の結果で空の引数n2を受けてしまってundefinedになっています。

15行目のreturn n2;は空っぽのままでも次の.always()が実行されてしまいます。

今度はreturnで新たに作ったDeferredオブジェクトを返すようにしてみます。

テストコード⑦
<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;
    setTimeout(function(){
      fn1(n);
      n2="で.alwaysも実行";
      df2.resolve(n2);
      }, 1000);
     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>
10行目でdf2というDeferredオブジェクトを生成し、17行目でdf2を返しています。

1000ミリ秒後にdf2をresolveし引数n2を渡しています。


「完了」をクリックするとちゃんと.1000ミリ秒後にthen()の結果が最初にでてから.always()の結果が次にでてきます。

df2.resolve(n2);の引数n2もちゃんとdf1.always()に引き継がれています。

df1のfulfilledのPromiseがdf2のunfulfilledのPromiseに置き換えられたかのような動きです。

でもdf2がresolveされる前にdf1.state()で確認してみてもやっぱりdf1はresolvedのままですけど。

Promiseを引き継がないjQuery1.7の.then()でテストコード⑦を実行してみるとjQuery1.8以降とは違ってPromiseが引き継がれず.always()の結果が最初に表示されてから1000ミリ秒後に.then()の結果が表示されます。


これでjQuery1.8以降のdeferred.then()が、.done()などと違うことがわかったでしょうか?

参考にしたサイト


Deferred Object | jQuery API Documentation
Promiseを実装したjQuery.Deferredの本家の解説。各項目をクリックすると例もあります。

次の関連記事:jQuery.Deferred学習メモ(3)deferred.then()でPromise animationをreturnする

PR

0 件のコメント:

コメントを投稿