Birden çok ajax çağrısı için jQuery geri araması


132

Bir tıklama olayında üç ajax çağrısı yapmak istiyorum. Her ajax çağrısı farklı bir işlem yapar ve son bir geri arama için gereken verileri geri döndürür. Çağrıların kendileri birbirine bağımlı değildir, hepsi aynı anda gidebilir, ancak üçü de tamamlandığında son bir geri aranma olmasını istiyorum.

$('#button').click(function() {
    fun1();
    fun2();
    fun3();
//now do something else when the requests have done their 'success' callbacks.
});

var fun1= (function() {
    $.ajax({/*code*/});
});
var fun2 = (function() {
    $.ajax({/*code*/});
});
var fun3 = (function() {
    $.ajax({/*code*/});
});


Yanıtlar:


112

İşte yazdığım bir geri arama nesnesi, hepsi tamamlandığında tek bir geri aramayı tetiklemek için ayarlayabilir veya her birinin kendi geri aramasını yapıp hepsini tamamlandığında ateşlemesini sağlayabilirsiniz:

FARKINA VARMAK

JQuery 1.5+ sürümünden bu yana, ertelenmiş yöntemi başka bir yanıtta açıklandığı gibi kullanabilirsiniz:

  $.when($.ajax(), [...]).then(function(results){},[...]);

Burada ertelenmiş örnek

jQuery <1.5 için aşağıdakiler çalışacaktır veya ajax çağrılarınızın bilinmeyen zamanlarda, burada iki düğmeyle gösterildiği gibi tetiklenmesi gerekiyorsa : her iki düğme tıklandıktan sonra tetiklenir

[Kullanımı]

tamamlandıktan sonra tek geri arama için : Çalışma Örneği

// initialize here
var requestCallback = new MyRequestsCompleted({
    numRequest: 3,
    singleCallback: function(){
        alert( "I'm the callback");
    }
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});

Hepsi tamamlandığında her birinin kendi geri araması vardır: Çalışma Örneği

//initialize 
var requestCallback = new MyRequestsCompleted({
    numRequest: 3
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the first callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the second callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the third callback');
        });
    }
});

[Kod]

var MyRequestsCompleted = (function() {
    var numRequestToComplete, requestsCompleted, callBacks, singleCallBack;

    return function(options) {
        if (!options) options = {};

        numRequestToComplete = options.numRequest || 0;
        requestsCompleted = options.requestsCompleted || 0;
        callBacks = [];
        var fireCallbacks = function() {
            alert("we're all complete");
            for (var i = 0; i < callBacks.length; i++) callBacks[i]();
        };
        if (options.singleCallback) callBacks.push(options.singleCallback);

        this.addCallbackToQueue = function(isComplete, callback) {
            if (isComplete) requestsCompleted++;
            if (callback) callBacks.push(callback);
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.requestComplete = function(isComplete) {
            if (isComplete) requestsCompleted++;
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.setCallback = function(callback) {
            callBacks.push(callBack);
        };
    };
})();

1
Harika kod örneği için teşekkürler! Sanırım geri kalanı boyunca kendi yolumdan geçebileceğimi düşünüyorum. Tekrar teşekkürler!
MisterIsaak

Sorun yok, yardımcı olduğuna sevindim! Kendimi biraz kaptırdım: P'nin hala bunun için bazı fikirleri var. Başlatma sırasında bir dizi istek belirtmek zorunda kalmayacağınız şekilde güncellemeyi planlıyorum.
pusula

Güzel, umarım yaparsın! Çok ilginç, ek bir geri arama ekleme seçeneğini eklemenizi öneririm. Yaptım ve yapmak istediğim şey için mükemmel çalışıyor. Tekrar teşekkürler!
MisterIsaak

Yaptım ama kullanım durumuna eklemeyi unuttum: setCallbackKuyruğa daha fazlasını eklemek için P kullanın . Gerçi şimdi düşünüyorum da ... Onu addCallbackya da
benzeri bir

Mi singleCallback2 çizgisinde değişken gereksiz? Yoksa bir şey mi kaçırıyorum?
nimcap

158

Görünüşe göre buna bazı cevaplarınız var, ancak burada kodunuzu büyük ölçüde basitleştirecek bahsetmeye değer bir şey olduğunu düşünüyorum. jQuery $.when, v1.5'i tanıttı . Şöyle görünüyor:

$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //this callback will be fired once all ajax calls have finished.
});

Burada bahsedildiğini görmedim, umarım yardımcı olur.


6
Cevap için teşekkürler, kesinlikle benim için gitmenin yolu bu. jQuery kullanır, kompakt, kısa ve anlamlıdır.
nimcap

2
Subhaze'in cevabı harika ama gerçekten doğru olan bu.
Molomby

9
JQuery 1.5+ sürümüne sahipseniz bunun iyi bir çözüm olduğuna katılıyorum, ancak yanıt anında ertelenmiş işlevsellik mevcut değildi. :(
subhaze

13

Kendime zarar veren herhangi bir nesneye ihtiyaç olduğunu görmüyorum. Basit, tam sayı olan bir değişkene sahiptir. Bir istek başlattığınızda, sayıyı artırın. Biri tamamladığında onu azalt. Sıfır olduğunda, devam eden istek yoktur, yani işiniz biter.

$('#button').click(function() {
    var inProgress = 0;

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what's in here when all requests have completed.
        }
    };

    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
});

Bu çok basit ve mükemmel çalışıyor ... Eğer (--İlerleme) durumunda ne olacağını biraz daha iyi açıklayabilir misiniz? Değişken inProgress == 0 olup olmadığını kontrol etmeli ve ona bir birim çıkarmalı ama kompakt sözdizimini alamıyorum ...
Pitto

1
@Pitto: Oops ... Aslında orada bir mantık hatası var ve olmalı if (!--inProgress). Doğru sürüm ile benzerdir inProgress = inProgress - 1; if (inProgress == 0) { /* do stuff */ }. Kompakt form, önek azaltma işlecini ( --) ve olumsuzlama işlecini ( !) birleştirerek "doğru" olarak değerlendirir (ve dolayısıyla ifbloğu yürütür ), eğer azaltma inProgresssonuçlanırsa inProgress == 0ve aksi takdirde "yanlış" olarak değerlendirir (ve dolayısıyla hiçbir şey yapmaz).
Matt

Tek kelimeyle harika! Zamanınız ve sabrınız için teşekkürler.
Pitto

@Pitto: Humm .. kullandığınız koda bağlantı verebilir misiniz? Benim için çalışıyor .
Matt

10

Belirterek It değerinde olduğunu beri $.whentüm sıralı argüman olarak ajax istekleri (değil bir dizi), sık görürsünüz beklentisini $.whenile kullanıldığında .apply()şöyle:

// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
    return $.ajax({
        method: 'GET',
        url: 'thing/' + thing.id
    });
});
$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
    // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
    var responseArgsArray = Array.prototype.slice.call(this, arguments);

});

Bunun nedeni $.when, böyle bağımsız değişkenleri kabul etmesidir

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

Ve bunun gibi değil:

$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);

Harika yanıt. Şu anda pek çok iyi sözlü kitap var, bunların çoğunun bir allyöntemi veya benzer bir şeyi var, ancak harita konseptini seviyorum. Güzel.
Greg

2
Evet, hemen hemen her zaman $ .when kullanırım üzerine bir dizi istek uygularken ve bunu burada bir çözümde görmedim, bu yüzden buraya iyi bir örnek olması için ekledim.
Cory Danielson

4

Hvgotcodes'in fikrini seviyorum. Benim önerim, tamamlanan sayıyı gereken sayı ile karşılaştıran ve ardından son geri aramayı çalıştıran genel bir artırıcı eklemek. Bu, son geri aramanın içine yerleştirilebilir.

var sync = {
 callbacksToComplete = 3,
 callbacksCompleted = 0,
 addCallbackInstance = function(){
  this.callbacksCompleted++;
  if(callbacksCompleted == callbacksToComplete) {
   doFinalCallBack();
  }
 }
};

[Ad güncellemelerini yansıtacak şekilde düzenlendi.]


^ Kabul edildi, gerçekte neye ihtiyacım olduğunu daha iyi anlamam yardımcı oldu.
MisterIsaak

3

DÜZENLE - belki de en iyi seçenek, üç isteğin yaptığı her şeyi yapan bir hizmet uç noktası oluşturmaktır. Bu şekilde, yalnızca bir istek yapmanız gerekir ve tüm veriler, yanıtta olması gereken yerdedir. Aynı 3 isteği defalarca yaptığınızı fark ederseniz, muhtemelen bu rotaya gitmek isteyeceksiniz. Sunucuda, yaygın olarak kullanılan daha küçük sunucu eylemlerini bir araya toplayan bir cephe hizmeti kurmak genellikle iyi bir tasarım kararıdır. Sadece bir fikir.


bunu yapmanın bir yolu, ajax çağrılarından önce tıklama işleyicinizde bir 'senkronizasyon' nesnesi oluşturmaktır. Gibi bir şey

var sync = {
   count: 0
}

Senkronizasyon, başarılı çağrıların kapsamına otomatik olarak bağlanacaktır (kapanış). Başarı işleyicide, sayımı artırırsınız ve 3 ise diğer işlevi çağırabilirsiniz.

Alternatif olarak, şöyle bir şey yapabilirsiniz:

var sync = {
   success1Complete: false,
   ...
   success3Complete: false,
}

her başarı yürütüldüğünde, senkronizasyondaki değeri true olarak değiştirir. Devam etmeden önce üçünün de doğru olduğundan emin olmak için senkronizasyonu kontrol etmeniz gerekir.

Xhr'lerinizden birinin başarı getirmediği duruma dikkat edin - bunu hesaba katmanız gerekir.

Yine başka bir seçenek, başarı işleyicilerinizde her zaman son işlevi çağırmak ve gerçekten bir şey yapıp yapmayacağınızı belirlemek için eşitleme seçeneğine erişmesini sağlamaktır. Yine de, senkronizasyonun bu işlev kapsamında olduğundan emin olmanız gerekir.


Birkaç nedenden ötürü ilk önerinizi daha çok beğeniyorum. Her isteğin farklı bir amacı vardır, bu nedenle onları bu nedenle ayrı tutmak istiyorum. Ayrıca istekler aslında bir işlevde çünkü onları başka yerlerde kullanıyorum, bu yüzden mümkünse modüler bir tasarım tutmaya çalışıyordum. Yardımınız için teşekkürler!
MisterIsaak

@Jisaak, evet, sunucuya bununla başa çıkmak için bir yöntem koymak sizin için mantıklı gelmeyebilir. Ancak sunucuda bir cephe oluşturmak kabul edilebilir. Modülerlikten bahsediyorsunuz, üç şeyi birden ele alan bir yöntem yaratmak modülerdir.
hvgotkodları


0

Bu sayfadaki cevaplardan bazı iyi ipuçları aldım. Biraz kendi kullanımım için uyarladım ve paylaşabileceğimi düşündüm.

// lets say we have 2 ajax functions that needs to be "synchronized". 
// In other words, we want to know when both are completed.
function foo1(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo1');
            callback();               
        }
    });
}

function foo2(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo2');
            callback();
        }
    });
}

// here is my simplified solution
ajaxSynchronizer = function() {
    var funcs = [];
    var funcsCompleted = 0;
    var callback;

    this.add = function(f) {
        funcs.push(f);
    }

    this.synchronizer = function() {
        funcsCompleted++;
        if (funcsCompleted == funcs.length) {
            callback.call(this);
        }
    }

    this.callWhenFinished = function(cb) {
        callback = cb;
        for (var i = 0; i < funcs.length; i++) {
            funcs[i].call(this, this.synchronizer);
        }
    }
}

// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
    alert('All done!');
}

// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);

Burada bazı sınırlamalar var ama benim durumum için sorun yoktu. Ayrıca daha gelişmiş şeyler için faydalı olabilecek bir AOP eklentisi (jQuery için) olduğunu da buldum: http://code.google.com/p/jquery-aop/


0

Bugün bu problemle karşılaştım ve bu, kabul edilen cevabı izlemeden önceki safça girişimimdi.

<script>
    function main() {
        var a, b, c
        var one = function() {
            if ( a != undefined  && b != undefined && c != undefined ) {
                alert("Ok")
            } else {
                alert( "¬¬ ")
            }
        }

        fakeAjaxCall( function() {
            a = "two"
            one()
        } )
        fakeAjaxCall( function() {
            b = "three"
            one()
        } )
        fakeAjaxCall( function() {
            c = "four"
            one()
        } )
    }
    function fakeAjaxCall( a ) {
        a()
    }
    main()
</script>

0

Bu jquery değil (ve jquery'nin uygulanabilir bir çözümü var gibi görünüyor) ama sadece başka bir seçenek gibi ...

SharePoint web hizmetleriyle yoğun bir şekilde çalışırken benzer sorunlar yaşadım - tek bir işlem için girdi oluşturmak üzere genellikle birden çok kaynaktan veri çekmeniz gerekir.

Bunu çözmek için AJAX soyutlama kitaplığıma bu tür bir işlevsellik ekledim. Tamamlandığında bir dizi işleyiciyi tetikleyecek bir isteği kolayca tanımlayabilirsiniz. Ancak her istek birden fazla http çağrısı ile tanımlanabilir. İşte bileşen (ve ayrıntılı belgeler):

DPAJAX DepressedPress.com'da

Bu basit örnek, üç çağrı içeren bir istek oluşturur ve ardından bu bilgileri çağrı sırasına göre tek bir işleyiciye aktarır:

    // The handler function 
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) }; 

    // Create the pool 
myPool = DP_AJAX.createPool(); 

    // Create the request 
myRequest = DP_AJAX.createRequest(AddUp); 

    // Add the calls to the request 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]); 

    // Add the request to the pool 
myPool.addRequest(myRequest); 

Bu yöntemin sağlanan diğer çözümlerin çoğunun (jquery'deki "ne zaman" çözümüne inanıyorum) aksine, yapılan aramaların tek bir dizisini zorlamadığını unutmayın - her biri yine de ortamın izin verdiği kadar hızlı (veya yavaş) çalışacaktır. ancak tek işleyici yalnızca tümü tamamlandığında çağrılacaktır. Ayrıca, hizmetiniz biraz kesintili ise zaman aşımı değerlerinin ayarlanmasını ve yeniden deneme girişimlerini destekler.

Bunu delice yararlı buldum (ve kod açısından anlamak inanılmaz derecede basit). Artık zincirleme yok, çağrıları saymak ve çıktıları kaydetmek yok. Sadece ayarla ve unut".


0

Tamam, bu eski ama lütfen çözümüme katkıda bulunayım :)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

Geri araması olan işlevlerle de çalışır. SyncCount'u ayarlarsınız ve her eylemin geri aramasında sync (...) işlevini çağırırsınız.


0

Sırayı düzenleyen ekstra yöntemlere ihtiyaç duymadan bunu yapmanın daha kolay bir yolunu buldum.

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP (ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP (ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

-1
async   : false,

Varsayılan olarak, tüm istekler eşzamansız olarak gönderilir (yani bu, varsayılan olarak doğru seçeneğine ayarlanmıştır). Eşzamanlı isteklere ihtiyacınız varsa, bu seçeneği olarak ayarlayın false. Etki alanları arası istekler ve dataType: "jsonp"istekler eşzamanlı işlemi desteklemez. Eşzamanlı isteklerin tarayıcıyı geçici olarak kilitleyebileceğini ve istek etkinken herhangi bir işlemi devre dışı bırakabileceğini unutmayın. İtibariyle jQuery 1.8 kullanımı async: falseile jqXHR ( $.Deferred) kaldırılmış olduğu; veya kullanımdan kaldırılan gibi jqXHR nesnesinin karşılık gelen yöntemleri yerine başarı / hata / tam geri arama seçeneklerini kullanmanız gerekir .jqXHR.done()jqXHR.success()


Bu yeterli bir çözüm değil. Asla eşzamanlı bir Ajax isteğinde bulunmamalısınız.
user128216

-2
$.ajax({type:'POST', url:'www.naver.com', dataType:'text', async:false,
    complete:function(xhr, textStatus){},
    error:function(xhr, textStatus){},
    success:function( data ){
        $.ajax({type:'POST', 
            ....
            ....
            success:function(data){
                $.ajax({type:'POST',
                    ....
                    ....
            }
        }
    });

Üzgünüm ama ne yaptığımı açıklayamam çünkü ben ingilizce tek kelime bile konuşamayan bir Koreliyim. ama kolayca anlayabileceğinizi düşünüyorum.


Ajax'ı bu şekilde kullanmamalısınız. Callback Hell ..!
traeper
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.