jQueryのdeferredオブジェクトの詳細(一)

20897 ワード

最近、jqueryの中の$を研究しています.Deferedの対象は、数日も分からないが、その中のソースコードの运行メカニズムは、ネット上で関连する资料を探して、『jQueryのdeferredオブジェクトの詳細』チェン一峰先生の文章、中でdeferredが言ったことを非常にはっきり述べて、私にも大いに悟らせて、后でよく调べるために、今チェン先生の文字を転载します.
一、deferredオブジェクトとは何ですか.
Webサイトを開発する過程で、Javascript操作に時間がかかることがよくあります.このうち、ajaxがサーバデータを読み出すなどの非同期操作もあれば、大きな配列を巡るなどの同期操作もあり、すぐに結果が得られるわけではありません.
通常、コールバック関数(callback)を指定します.つまり、実行が終了すると、どの関数を呼び出すべきかを事前に規定しています.
しかし,コールバック関数ではjQueryの機能は非常に弱い.これを変更するために、jQuery開発チームはdeferredオブジェクトを設計しました.
簡単に言えば、deferredオブジェクトはjQueryのコールバック関数ソリューションです.英語ではdeferは「遅延」を意味するため、deferredオブジェクトの意味は「遅延」を将来のある点に実行することである.
時間のかかる操作をどのように処理するかという問題を解決し、それらの操作に対してより良い制御を提供し、統一的なプログラミングインターフェースを提供します.その主な機能は、4点にまとめることができます.次に、サンプルコードを用いて、一歩一歩勉強します.
  
二、ajax操作のチェーン書き方
まず、jQueryのajax操作の伝統的な書き方を振り返ってみましょう.
 $.ajax({
    url: "test.html",
    success: function(){
      alert("  ,   !");
    },
    error:function(){
      alert("");
    }
  });

コード 1

上のコードでは$.ajax()は、successメソッドが操作に成功した後のコールバック関数を指定し、errorメソッドが操作に失敗した後のコールバック関数を指定する2つのメソッドを含むオブジェクトパラメータを受け入れます.
  $.ajax()操作が完了した後、1.5.0未満のjQueryを使用している場合は、XHRオブジェクトを返します.チェーン操作はできません.バージョン1.5.0より高い場合はdeferredオブジェクトが返され、チェーン操作が可能です.
今、新しい書き方はこうです.
$.ajax("test.html")
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });

コード 2

done()はsuccessメソッドに相当し、fail()はerrorメソッドに相当することがわかる.チェーン書き方を採用すると,コードの可読性が大幅に向上する.
三、同一操作の複数のコールバック関数を指定する
deferredオブジェクトの大きなメリットは、複数のコールバック関数を自由に追加できることです.
それとも上のコードを例にとると、ajax操作が成功した後、元のコールバック関数のほかに、もう一つのコールバック関数を実行したいのですが、どうすればいいですか?
簡単です.直接後ろに加えればいいです.
  $.ajax("test.html")
  .done(function(){ alert("  ,   !");} )
  .fail(function(){ alert(""); } )
  .done(function(){ alert("");} );

  ( コード 3

コールバック関数は、追加順に実行される任意の複数を追加できます.
四、複数の操作にコールバック関数を指定する
deferredオブジェクトのもう一つの大きな利点は、従来の書き方ではできない複数のイベントにコールバック関数を指定できることです.
新しい方法$.when()を使用したコードを見てください.
 $.when($.ajax("test1.html"), $.ajax("test2.html"))
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });

  ( コード 4

このコードは、まず2つの操作$を実行することを意味する.ajax(「test 1.html」)と$.ajax(「test 2.html」)は、成功したらdone()で指定したコールバック関数を実行します.失敗したか失敗した場合はfail()で指定したコールバック関数を実行します.
五、普通操作のコールバック関数インタフェース(上)
deferredオブジェクトの最大の利点は、ajax操作からすべての操作にこのコールバック関数インタフェースを拡張することです.すなわち、ajax操作でもローカル操作でも非同期操作でも同期操作でもdeferredオブジェクトの様々な方法を使用してコールバック関数を指定できます.
具体的な例を見てみましょう.時間のかかる操作waitがあると仮定します.
var wait = function(){
    var tasks = function(){
      alert("");
    };
    setTimeout(tasks,5000);
  };

コールバック関数を指定しますが、どうすればいいですか?
自然に、$を使うことができると思います.when():
 $.when(wait())
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });

コード 5

しかし,このように書くとdone()メソッドは直ちに実行され,コールバック関数としての役割は果たされない.原因は$.when()のパラメータはdeferredオブジェクトのみなので、wait()を書き換える必要があります:
 var dtd = $.Deferred(); //     deferred  
  var wait = function(dtd){
    var tasks = function(){
      alert("");
      dtd.resolve(); //   deferred       
    };
    setTimeout(tasks,5000);
    return dtd;
  };

これでwait()関数はdeferredオブジェクトを返し、チェーン操作を加えることができます.
$.when(wait(dtd))
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });
コード 6

wait()関数の実行が完了するとdone()メソッドで指定したコールバック関数が自動的に実行されます.
六、deferred.resolve()メソッドとdeferred.reject()メソッド
よく見ると、上のwait()関数の中に、もう一つ説明していないところがあります.dtd.resolve()の役割は何ですか?
この問題を明らかにするには、新しい概念「実行状態」を導入しなければならない.jQueryでは、deferredオブジェクトには、未完了、完了、失敗の3つの実行状態があることを規定しています.実行ステータスが「完了」(resolved)の場合、deferredオブジェクトはdone()メソッドで指定されたコールバック関数をすぐに呼び出します.実行ステータスが「失敗しました」の場合、fail()メソッドで指定されたコールバック関数を呼び出します.実行ステータスが「未完了」の場合は待ち続けるか、progress()メソッドで指定されたコールバック関数(jQuery 1.7バージョン追加)を呼び出します.
前の部分のajax操作時、deferredオブジェクトは戻り結果に基づいて、自動的に自身の実行状態を変更します.ただし、wait()関数では、この実行状態はプログラマが手動で指定する必要があります.dtd.resolve()は、dtdオブジェクトの実行状態を「未完了」から「完了」に変更し、done()メソッドをトリガーすることを意味する.
同様に、dtdオブジェクトの実行状態を「未完了」から「失敗」に変更してfail()メソッドをトリガするdeferred.reject()メソッドも存在する.
  var dtd = $.Deferred(); //     Deferred  
  var wait = function(dtd){
    var tasks = function(){
      alert("");
      dtd.reject(); //   Deferred       
    };
    setTimeout(tasks,5000);
    return dtd;
  };
  $.when(wait(dtd))
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });

コード 7

七、deferred.promise()メソッド
上のような書き方は、やはり問題があります.それはdtdがグローバルオブジェクトであるため、その実行状態は外部から変更することができる.
次のコードを見てください.
  var dtd = $.Deferred(); //     Deferred  
  var wait = function(dtd){
    var tasks = function(){
      alert("");
      dtd.resolve(); //   Deferred       
    };
    setTimeout(tasks,5000);
    return dtd;
  };
  $.when(wait(dtd))
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });
  dtd.resolve();

コード 8

 
コードの末尾にdtdを1行追加しました.resolve()は、dtdオブジェクトの実行状態を変更するため、done()メソッドがすぐに実行され、「ははは、成功した!」のプロンプトボックスで、5秒待ってから「実行完了!に表示されます.
 
このような状況を回避するために、jQueryはdeferred.promise()の方法を提供する.その役割は、元のdeferredオブジェクトに別のdeferredオブジェクトを返し、後者は実行状態の変更に関係のないメソッド(done()メソッドやfail()メソッドなど)のみを開放し、実行状態の変更に関連するメソッド(resolve()メソッドやreject()メソッドなど)を遮断して実行状態を変更できないようにすることです.
注意:
  deferred.promise()は、このdeferredオブジェクトの状態を変更するために他のコードをブロックするだけです.deferred.promise()メソッドが返すdeferredオブジェクトは、resolve,reject,progress,resolveWith,rejectWith,progressWithといった状態を変える方法がなく、done,then,failなどのメソッドでhandlerを追加したり、状態を判断したりするしかありません.
  deferred.promise()はdeferredオブジェクトの状態を変えることができず、作用も現在の状態が変わらないことを保証するのではなく、deferredを通過できないことを保証するだけです.promise()が返すdeferred promiseオブジェクトはdeferredオブジェクトの状態を変更します.
 
次のコードを見てください.
 var dtd = $.Deferred(); //     Deferred  
  var wait = function(dtd){
    var tasks = function(){
      alert("");
      dtd.resolve(); //   Deferred       
    };

    setTimeout(tasks,5000);
    return dtd.promise(); //   promise  
  };
  var d = wait(dtd); //     d  ,           
  $.when(d)
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });
  d.resolve(); //

コード 9

 
 
上記のコードでは、wait()関数はpromiseオブジェクトを返します.次に、元のdeferredオブジェクトではなく、コールバック関数をこのオブジェクトにバインドします.このような利点は,このオブジェクトの実行状態を変えることができず,実行状態を変えるには元のdeferredオブジェクトしか操作できないことである.
 
ただし、より良い書き方はallenmで指摘されており、dtdオブジェクトをwait()関数の内部オブジェクトにする.
 var wait = function(dtd){
    var dtd = $.Deferred(); //     ,    Deferred  
    var tasks = function(){
      alert("    !");
      dtd.resolve(); //   Deferred       
    };

    setTimeout(tasks,5000);
    return dtd.promise(); //   promise  
  };
  $.when(wait())
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert("   !"); });
コード 10

八、普通操作のコールバック関数インタフェース(中)
もう1つの実行状態の外部変更を防止する方法は、deferredオブジェクトの構築関数$を用いる.Deferred().
このとき、wait関数は変わらず、私たちはそれを直接$に伝えます.Deferred():
 $.Deferred(wait)
  .done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });
コード 11

 
 
jQuery規定,$.Deferred()は、パラメータとして関数名(注意、関数名)を1つ受け入れることができる、$.Deferred()によって生成されたdeferredオブジェクトは、この関数のデフォルトパラメータとして使用されます.
 
九、普通操作のコールバック関数インタフェース(下)
 
上記の2つの方法に加えて、deferredインタフェースをwaitオブジェクトに直接配置することもできます.
 var dtd = $.Deferred(); //   Deferred  
  var wait = function(dtd){
    var tasks = function(){
      alert("");
      dtd.resolve(); //   Deferred       
    };
    setTimeout(tasks,5000);
  };
  dtd.promise(wait);
  wait.done(function(){ alert("  ,   !"); })
  .fail(function(){ alert(""); });
  wait(dtd);
コード 12

 
十、小結:deferredオブジェクトの方法
deferredオブジェクトのさまざまな方法について説明しましたが、以下にまとめます.
(1)$.Deferred() deferredオブジェクトを生成する.
(2)deferred.done()操作成功時のコールバック関数の指定
(3)deferred.fail()操作失敗時のコールバック関数の指定
(4)deferred.promise()パラメータがない場合、新しいdeferredオブジェクトが返され、そのオブジェクトの動作状態が変更できない.パラメータを受け入れると、パラメータオブジェクトにdeferredインタフェースを配置する役割を果たします.
(5)deferred.resolve() deferredオブジェクトの動作状態を手動で「完了」に変更し、done()メソッドを直ちにトリガする.
(6)deferred.reject()この方法はdeferred.resolve()は正反対で、呼び出し後にdeferredオブジェクトの実行状態を「失敗」に変更し、fail()メソッドを直ちにトリガーします.
(7)$.when()複数の操作にコールバック関数を指定する.
これらの方法に加えて、deferredオブジェクトには2つの重要な方法があります.上記のチュートリアルでは説明していません.
 
  (8) deferred.then()
時には手間を省くためにdone()とfail()を合わせて書くことができますが、これがthen()メソッドです.
 $.when($.ajax( "/main.php" ))
  .then(successFunc, failureFunc );

then()に2つのパラメータがある場合、最初のパラメータはdone()メソッドのコールバック関数であり、2番目のパラメータはfail()メソッドのコールバックメソッドである.then()にパラメータが1つしかない場合、done()に等しい.
  (9) deferred.always()
この方法はコールバック関数を指定するためにも使用され、呼び出しがdeferredであるにかかわらず.resolve()かdeferred.reject()は、最後に常に実行されます.
  $.ajax( "test.html" )
  .always( function() { alert("");} );