Gọi lại jQuery cho nhiều cuộc gọi ajax


132

Tôi muốn thực hiện ba cuộc gọi ajax trong một sự kiện nhấp chuột. Mỗi cuộc gọi ajax thực hiện một thao tác riêng biệt và trả về dữ liệu cần thiết cho cuộc gọi lại cuối cùng. Bản thân các cuộc gọi không phụ thuộc vào nhau, tất cả chúng có thể diễn ra cùng một lúc, tuy nhiên tôi muốn có một cuộc gọi lại cuối cùng khi cả ba hoàn thành.

$('#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*/});
});


Câu trả lời:


112

Đây là một đối tượng gọi lại mà tôi đã viết trong đó bạn có thể đặt một cuộc gọi lại duy nhất để kích hoạt một khi tất cả hoàn thành hoặc để mỗi người có cuộc gọi lại của riêng họ và kích hoạt tất cả khi tất cả hoàn thành:

ĐỂ Ý

Vì jQuery 1.5+, bạn có thể sử dụng phương thức hoãn lại như được mô tả trong câu trả lời khác:

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

Ví dụ về hoãn lại ở đây

đối với jQuery <1.5, các thao tác sau sẽ hoạt động hoặc nếu bạn cần thực hiện các cuộc gọi ajax của mình vào những thời điểm không xác định như được hiển thị ở đây với hai nút: được kích hoạt sau khi cả hai nút được nhấp

[sử dụng]

cho một cuộc gọi lại sau khi hoàn thành: Ví dụ làm việc

// 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);
    }
});

mỗi người có cuộc gọi lại riêng khi tất cả hoàn thành: Ví dụ làm việc

//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');
        });
    }
});

[Mật mã]

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
Cảm ơn ví dụ mã tuyệt vời! Tôi nghĩ rằng tôi sẽ có thể vấp ngã trong phần còn lại. Cảm ơn một lần nữa!
MisterIsaak

Không có thăm dò, vui mừng nó đã giúp! Tôi đã mang theo nó: P vẫn còn một số ý tưởng cho nó. Tôi dự định cập nhật nó vào nơi bạn không phải chỉ định một số yêu cầu khi khởi tạo.
subhaze

Tốt đẹp, tôi hy vọng bạn làm! Rất thú vị, tôi khuyên bạn nên đưa vào tùy chọn để thêm một cuộc gọi lại bổ sung. Tôi đã làm và nó hoạt động hoàn hảo cho những gì tôi muốn làm. Cảm ơn một lần nữa!
MisterIsaak

Tôi đã làm nhưng quên thêm nó vào trường hợp sử dụng: P sử dụng setCallbackđể thêm nhiều hơn vào hàng đợi. Mặc dù bây giờ tôi nghĩ về nó ... tôi nên gọi nó addCallbackhoặc một cái gì đó tương tự ... và không chỉ được thiết lập vì nó thêm vào hàng đợi
subhaze

singleCallbackbiến ở dòng thứ 2 không cần thiết? Hay tôi đang thiếu một cái gì đó?
nimcap

158

Có vẻ như bạn đã có một số câu trả lời cho điều này, tuy nhiên tôi nghĩ rằng có một điều đáng nói ở đây sẽ giúp đơn giản hóa rất nhiều mã của bạn. jQuery đã giới thiệu phiên bản $.whenv1.5. Nó có vẻ như:

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

Không thấy nó được đề cập ở đây, hy vọng nó sẽ giúp.


6
Cảm ơn câu trả lời, đây chắc chắn là con đường dành cho tôi. Nó sử dụng jQuery, nó nhỏ gọn, ngắn gọn và biểu cảm.
nimcap

2
Câu trả lời của Subhaze rất hay nhưng thực sự, đây là câu trả lời đúng.
Molomby

9
Tôi đồng ý đây là một giải pháp tốt khi bạn có jQuery 1.5+ nhưng tại thời điểm trả lời, chức năng trả lời không có sẵn. :(
subhaze

13

Bản thân tôi không thấy bất kỳ sự cần thiết nào. Đơn giản có một biến là một số nguyên. Khi bạn bắt đầu một yêu cầu, tăng số lượng. Khi một người hoàn thành, giảm nó. Khi nó bằng không, không có yêu cầu nào đang diễn ra, vậy là bạn đã hoàn thành.

$('#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
        }
    });
});

Điều này rất đơn giản và hoạt động hoàn hảo ... Bạn có phiền giải thích tốt hơn một chút về những gì xảy ra nếu (--inProjection) không? Nó sẽ kiểm soát nếu biến inProTHER == 0 và trừ đi một đơn vị cho nó nhưng tôi không nhận được cú pháp nhỏ gọn ...
Pitto

1
@Pitto: Rất tiếc ... thực sự, có lỗi logic ở đó và nó phải như vậy if (!--inProgress). Phiên bản chính xác tương tự như inProgress = inProgress - 1; if (inProgress == 0) { /* do stuff */ }. Dạng rút gọn kết hợp toán tử giảm tiền tố ( --) và toán tử phủ định ( !) để đánh giá thành "true" (và do đó thực thi ifkhối), nếu giảm inProgresskết quả inProgress == 0và đánh giá thành "false" (và do đó không có gì).
Matt

Đơn giản là khôn ngoan! Cảm ơn thời gian và sự kiên nhẫn của bạn.
Pitto

@Pitto: Humm .. bạn có thể liên kết đến mã bạn đang sử dụng không? Nó làm việc cho tôi .
Matt

10

Điều đáng chú ý là vì $.whenmong đợi tất cả các yêu cầu ajax là đối số tuần tự (không phải là một mảng) mà bạn thường thấy $.whenđược sử dụng .apply()như vậy:

// 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);

});

Điều này là do $.whenchấp nhận lập luận như thế này

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

Và không như thế này:

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

Phản ứng tuyệt vời. Hiện tại có rất nhiều libs hứa hẹn tốt, hầu hết chúng đều có một allphương pháp hoặc một cái gì đó tương tự, nhưng tôi thích khái niệm bản đồ. Đẹp.
Greg

2
Vâng, tôi hầu như luôn sử dụng $ .when bằng cách áp dụng một loạt các yêu cầu lên nó và không phát hiện ra rằng trong một giải pháp ở đây, vì vậy chỉ cần thêm nó để có một ví dụ hay ở đây.
Cory Danielson

4

Tôi thích ý tưởng của hvgotcodes. Đề nghị của tôi là thêm một số gia chung để so sánh số hoàn thành với số cần thiết và sau đó chạy cuộc gọi lại cuối cùng. Điều này có thể được xây dựng vào cuộc gọi lại cuối cùng.

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

[Đã chỉnh sửa để phản ánh cập nhật tên.]


^ Đồng ý, đã giúp hiểu rõ hơn về những gì tôi thực sự cần.
MisterIsaak

3

EDIT - có lẽ tùy chọn tốt nhất sẽ là tạo một điểm cuối dịch vụ thực hiện mọi thứ mà ba yêu cầu thực hiện. Bằng cách đó, bạn chỉ phải thực hiện một yêu cầu và tất cả dữ liệu là nơi bạn cần có trong phản hồi. Nếu bạn thấy bạn đang thực hiện cùng 3 yêu cầu lặp đi lặp lại, có lẽ bạn sẽ muốn đi theo con đường này. Nó thường là một quyết định thiết kế tốt để thiết lập một dịch vụ mặt tiền trên máy chủ kết hợp các hành động máy chủ nhỏ hơn thường được sử dụng cùng nhau. Chỉ là một ý tưởng.


một cách để làm điều đó là tạo một đối tượng 'đồng bộ hóa' trong trình xử lý nhấp chuột của bạn trước khi các cuộc gọi ajax. Cái gì đó như

var sync = {
   count: 0
}

Đồng bộ hóa sẽ được ràng buộc với phạm vi của các cuộc gọi thành công tự động (đóng). Trong trình xử lý thành công, bạn tăng số đếm và nếu là 3, bạn có thể gọi hàm khác.

Ngoài ra, bạn có thể làm một cái gì đó như

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

khi mỗi thành công được thực thi, nó sẽ thay đổi giá trị trong đồng bộ thành đúng. Bạn sẽ phải kiểm tra đồng bộ hóa để đảm bảo rằng cả ba đều đúng trước khi tiếp tục.

Lưu ý trường hợp một trong những xhrs của bạn không trả về thành công - bạn cần tính đến điều đó.

Tuy nhiên, một tùy chọn khác là luôn gọi hàm cuối cùng trong trình xử lý thành công của bạn và để nó truy cập tùy chọn đồng bộ hóa để xác định xem có thực sự làm gì không. Bạn sẽ cần đảm bảo rằng đồng bộ hóa nằm trong phạm vi của chức năng đó.


Tôi thích đề nghị đầu tiên của bạn nhiều hơn cho một vài lý do. Mỗi yêu cầu có một mục đích riêng biệt, vì vậy tôi muốn tách chúng ra vì lý do đó. Ngoài ra các yêu cầu thực sự nằm trong một chức năng vì tôi sử dụng chúng ở nơi khác, vì vậy tôi đã cố gắng giữ một thiết kế mô-đun nếu có thể. Cảm ơn bạn đã giúp đỡ!
MisterIsaak

@Jisaak, yeah đặt một phương thức trên máy chủ để đối phó với điều này có thể không có ý nghĩa đối với bạn. Nhưng nó được chấp nhận để thiết lập một mặt tiền trên máy chủ. Bạn nói về tính mô đun, tạo ra một phương thức xử lý cả ba thứ là mô đun.
hvgotcodes


0

Tôi có một số gợi ý tốt từ các câu trả lời trên trang này. Tôi đã điều chỉnh nó một chút để sử dụng và nghĩ rằng tôi có thể chia sẻ.

// 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);

Có một số hạn chế ở đây, nhưng đối với trường hợp của tôi thì không sao. Tôi cũng thấy rằng đối với những thứ nâng cao hơn, nó cũng có một plugin AOP (dành cho jQuery) có thể hữu ích: http://code.google.com.vn/p/jquery-aop/


0

Tôi đã gặp vấn đề này ngày hôm nay, và đây là nỗ lực ngây thơ của tôi trước khi xem câu trả lời được chấp nhận.

<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

Đó không phải là jquery (và có vẻ như jquery có một giải pháp khả thi) mà chỉ là một lựa chọn khác ....

Tôi đã gặp vấn đề tương tự khi làm việc nhiều với các dịch vụ web SharePoint - bạn thường cần phải lấy dữ liệu từ nhiều nguồn để tạo đầu vào cho một quy trình.

Để giải quyết nó, tôi đã nhúng loại chức năng này vào thư viện trừu tượng AJAX của mình. Bạn có thể dễ dàng xác định một yêu cầu sẽ kích hoạt một bộ xử lý khi hoàn thành. Tuy nhiên, mỗi yêu cầu có thể được xác định bằng nhiều cuộc gọi http. Đây là thành phần (và tài liệu chi tiết):

DPAJAX tại DepressionPress.com

Ví dụ đơn giản này tạo một yêu cầu với ba cuộc gọi và sau đó chuyển thông tin đó, theo thứ tự cuộc gọi, đến một trình xử lý duy nhất:

    // 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); 

Lưu ý rằng không giống như nhiều giải pháp khác (bao gồm, tôi tin rằng giải pháp "khi" trong jquery) được cung cấp phương pháp này không buộc luồng đơn của các cuộc gọi được thực hiện - mỗi giải pháp sẽ vẫn chạy nhanh (hoặc chậm) khi môi trường cho phép nhưng trình xử lý duy nhất sẽ chỉ được gọi khi tất cả đã hoàn thành. Nó cũng hỗ trợ cài đặt các giá trị thời gian chờ và thử lại nếu dịch vụ của bạn hơi thiếu sót.

Tôi đã tìm thấy nó cực kỳ hữu ích (và cực kỳ đơn giản để hiểu từ góc độ mã). Không còn xích, không còn đếm cuộc gọi và tiết kiệm đầu ra. Mới làm đã quên".


0

Ok, cái này đã cũ nhưng làm ơn hãy để tôi đóng góp giải pháp của mình :)

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 ); } )

Nó cũng hoạt động với các chức năng có một cuộc gọi lại. Bạn đặt syncCount và bạn gọi chức năng đồng bộ hóa (...) trong cuộc gọi lại của mọi hành động.


0

Tôi tìm thấy một cách dễ dàng hơn để làm điều đó mà không cần các phương pháp bổ sung sắp xếp hàng đợi.

Mã não

$.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,

Theo mặc định, tất cả các yêu cầu được gửi không đồng bộ (nghĩa là điều này được đặt thành đúng theo mặc định). Nếu bạn cần các yêu cầu đồng bộ, hãy đặt tùy chọn này thành false. Các yêu cầu và yêu cầu tên miền chéo dataType: "jsonp"không hỗ trợ hoạt động đồng bộ. Lưu ý rằng các yêu cầu đồng bộ có thể tạm thời khóa trình duyệt, vô hiệu hóa mọi hành động trong khi yêu cầu được kích hoạt. Kể từ jQuery 1.8 , việc sử dụng async: falsevới jqXHR ( $.Deferred) không được chấp nhận; bạn phải sử dụng các tùy chọn gọi lại thành công / lỗi / hoàn thành thay vì các phương thức tương ứng của đối tượng jqXHR như jqXHR.done()hoặc không dùng nữa jqXHR.success().


Đây không phải là một giải pháp thích hợp. Bạn không bao giờ nên thực hiện một yêu cầu Ajax đồng bộ.
dùng128216

-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',
                    ....
                    ....
            }
        }
    });

Tôi xin lỗi nhưng tôi không thể giải thích những gì tôi nói vì tôi là người Hàn Quốc không thể nói được một từ tiếng Anh. nhưng tôi nghĩ bạn có thể dễ dàng hiểu nó


Không nên sử dụng ajax theo cách này. Gọi lại địa ngục ..!
kẻ phản bội
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.