web-dev-qa-db-ja.com

jqueryを使用してajax呼び出しをチェーンする方法

ブラウザーをロックせずに一連のN Ajaxリクエストを作成する必要があり、jquery遅延オブジェクトを使用してこれを達成したいです。

3つのリクエストを使用した簡単な例を次に示しますが、私のプログラムは100を超えるキューを必要とする場合があります(これは正確なユースケースではないことに注意してください。実際のコードは、次のステップ):

$(document).ready(function(){

    var deferred = $.Deferred();

    var countries = ["US", "CA", "MX"];

    $.each(countries, function(index, country){

        deferred.pipe(getData(country));

    });

 });

function getData(country){

    var data = {
        "country": country  
    };


    console.log("Making request for [" + country + "]");

    return $.ajax({
        type: "POST",
        url: "ajax.jsp",
        data: data,
        dataType: "JSON",
        success: function(){
            console.log("Successful request for [" + country + "]");
        }
    });

}

コンソールに書き込まれる内容は次のとおりです(すべての要求は並行して行われ、応答時間は予想どおり各国のデータのサイズに直接比例します。

Making request for [US]
Making request for [CA]
Making request for [MX]
Successful request for [MX]
Successful request for [CA]
Successful request for [US]

遅延オブジェクトを取得してこれらをキューに入れるにはどうすればよいですか?パイプへの変更を試みましたが、同じ結果が得られます。

望ましい結果は次のとおりです。

Making request for [US]
Successful request for [US]
Making request for [CA]
Successful request for [CA]
Making request for [MX]
Successful request for [MX]

編集:

リクエストパラメータを格納するために配列を使用することをお勧めしますが、jquery遅延オブジェクトにはリクエストをキューイングする機能があり、この機能を最大限に活用する方法を学びたいです。

これは事実上私がやろうとしていることです:

when(request[0]).pipe(request[1]).pipe(request[2])... pipe(request[N]);

ただし、各トラバーサルを効果的に使用するために、一度に1ステップずつリクエストをパイプに割り当てたいと思います。

deferred.pipe(request[0]);
deferred.pipe(request[1]);
deferred.pipe(request[2]);
33
Graham

カスタムオブジェクトあり

_function DeferredAjax(opts) {
    this.options=opts;
    this.deferred=$.Deferred();
    this.country=opts.country;
}
DeferredAjax.prototype.invoke=function() {
    var self=this, data={country:self.country};
    console.log("Making request for [" + self.country + "]");

    return $.ajax({
        type: "GET",
        url: "wait.php",
        data: data,
        dataType: "JSON",
        success: function(){
            console.log("Successful request for [" + self.country + "]");
            self.deferred.resolve();
        }
    });
};
DeferredAjax.prototype.promise=function() {
    return this.deferred.promise();
};


var countries = ["US", "CA", "MX"], startingpoint = $.Deferred();
startingpoint.resolve();

$.each(countries, function(ix, country) {
    var da = new DeferredAjax({
        country: country
    });
    $.when(startingpoint ).then(function() {
        da.invoke();
    });
    startingpoint= da;
});
_

フィドル http://jsfiddle.net/7kuX9/1/

もう少し明確にするために、最後の行を書くことができます

_c1=new DeferredAjax( {country:"US"} );
c2=new DeferredAjax( {country:"CA"} );
c3=new DeferredAjax( {country:"MX"} );

$.when( c1 ).then( function() {c2.invoke();} );
$.when( c2 ).then( function() {c3.invoke();} );
_

パイプ付き

_function fireRequest(country) {
        return $.ajax({
            type: "GET",
            url: "wait.php",
            data: {country:country},
            dataType: "JSON",
            success: function(){
                console.log("Successful request for [" + country + "]");
            }
        });
}

var countries=["US","CA","MX"], startingpoint=$.Deferred();
startingpoint.resolve();

$.each(countries,function(ix,country) {
    startingpoint=startingpoint.pipe( function() {
        console.log("Making request for [" + country + "]");
        return fireRequest(country);
    });
});
_

http://jsfiddle.net/k8aUj/1/

編集:結果ウィンドウにログを出力するフィドル http://jsfiddle.net/k8aUj/3/

各パイプ呼び出しは新しいプロミスを返し、それは次のパイプで使用されます。私はsccess関数のみを提供したことに注意してください。障害に対しても同様の関数を提供する必要があります。

各ソリューションでは、Ajax呼び出しは関数でラップすることにより必要になるまで遅延され、リスト内の各アイテムに対して新しいプロミスが作成されてチェーンが構築されます。

カスタムオブジェクトを使用すると、チェーンを簡単に操作できますが、パイプを使用する方が好みに合っていると思います。

:jQuery 1.8以降、 deferred.pipe() は非推奨、 _deferred.then_ はそれを置き換えます。

31
nikoshr

注:jquery 1.8では、_.then_の代わりに_.pipe_を使用できます。 _.then_関数は新しいpromiseを返すようになり、_.pipe_は不要になったため廃止されました。 promiseの詳細については promises spec を、jquery依存関係のないjavascript promiseのよりクリーンなライブラリーについては q.js を参照してください。

_countries.reduce(function(l, r){
  return l.then(function(){return getData(r)});
}, $.Deferred().resolve());
_

q.jsを使用する場合:

_//create a closure for each call
function getCountry(c){return function(){return getData(c)};}
//fire the closures one by one
//note: in Q, when(p1,f1) is the static version of p1.then(f1)
countries.map(getCountry).reduce(Q.when, Q());
_

元の答え:

さらに別のパイプ。気弱な人向けではなく、もう少しコンパクトです。

_countries.reduce(function(l, r){
  return l.pipe(function(){return getData(r)});
}, $.Deferred().resolve());
_

ドキュメントの削減 は、おそらく上記のコードがどのように機能するかを理解するのに最適な場所です。基本的に、コールバックと初期値の2つの引数を取ります。

コールバックは配列のすべての要素に繰り返し適用されます。最初の引数には前の反復の結果が渡され、2番目の引数は現在の要素になります。ここでのコツは、getData()jquery deferred promise を返し、パイプがgetDataが現在の要素で呼び出される前に前の要素のgetDataが完了することを確認することです。

2番目の引数$.Deferred().resolve()は、解決された遅延値のイディオムです。これは、コールバック実行の最初の反復に送られ、最初の要素のgetDataがすぐに呼び出されるようにします。

5
topkara

なぜあなたがこれをしたいのか正確にはわかりませんが、要求する必要があるすべてのURLのリストを保持し、success関数が呼び出されるまで次のURLを要求しないでください。つまり、successは条件付きでdeferredへの追加の呼び出しを行います。

4
ziesemer

私はこれに遅れていることは知っていますが、元のコードはほとんど問題ありませんが、2つの(おそらく3つの)問題があると思います。

パイプのパラメーターをどのようにコーディングしたかにより、getData(country)がすぐに呼び出されます。 getData()はすぐに実行され、結果(ajaxの約束ですが、httpリクエストはすぐに開始されます)がパラメーターとしてpipe()に渡されます。したがって、コールバック関数を渡す代わりに、オブジェクトを渡すことになります-これにより、パイプの新しい遅延がすぐに解決されます。

私はそれがする必要があると思う

deferred.pipe(function () { return getData(country); });

これは、パイプの親の遅延が解決されたときに呼び出されるコールバック関数です。この方法でコーディングすると、2番目の問題が発生します。延期されたマスターが解決されるまで、getData()は実行されません。

潜在的な3番目の問題は、すべてのパイプが延期されたマスターに接続されるため、実際にはチェーンがなく、とにかくそれらをすべて同時に実行できるのではないかということです。ドキュメントには、コールバックは順番に実行されると書かれていますが、コールバックはプロミスを返し、非同期で実行されるため、それらはすべてある程度並行して実行される可能性があります。

だから、このようなものが必要だと思う

var countries = ["US", "CA", "MX"];
var deferred = $.Deferred();
var promise = deferred.promise();

$.each(countries, function(index, country) {
    promise = promise.pipe(function () { return getData(country); });
});

deferred.resolve();
4
Jeff Shepler

JQueryキューで成功しました。

$(function(){
    $.each(countries, function(i,country){
      $('body').queue(function() {
        getData(country);
      });
    });
});

var getData = function(country){
  $.ajax({
    url : 'ajax.jsp',
    data : { country : country },
    type : 'post',
    success : function() {                          
      // Que up next ajax call
      $('body').dequeue();
    },
    error : function(){
      $('body').clearQueue();
    }
  });
};
3
Daniel Bardi

更新:deferred.pipeは非推奨です

これは、jQuery APIで既に文書化されているものの多くのコードです。 http://api.jquery.com/deferred.pipe/ を参照してください

100個すべてが作成されるまで、それらをパイピングし続けることができます。

または、N個の呼び出しを行い、行われたすべての呼び出しのデータを使用して単一の関数を解決するために何かを作成しました。注:スーパーXHRオブジェクトではなくデータを返します。 https://Gist.github.com/1219564

2
Drew