Цепочка ajax-запросов с отложенным jQuery

У меня есть веб-приложение, которое должно вызывать сервер несколько раз. До сих пор у меня была длинная вложенная цепочка обратного вызова; но я хотел бы использовать функции jQuery when, then и т.д. Однако после использования then я не могу показаться, что все работает снова.

$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
    // This works fine
    alert(args);
    $('#content').replaceWith (args);
    $('#progress-bar').progressbar ({value: 0});
})
.then ($.get('pages/test.html'))
.done (function(args)
{
    // This prints the same as the last call
    alert (args);
});

Что я делаю неправильно? Я предполагаю, что это проблема, связанная с определением области видимости, поскольку я вижу, как выполняется второй вызов get. Использование двух разных переменных args не помогает, поскольку аргумент, переданный выполняемой функции, по-прежнему является первым запросом get.

Ответ 1

Как обновление:

С помощью современного jquery (1.8+) вам не требуется предварительное, потому что get возвращает Отложенное обещание.

Кроме того, протокол устарел. Вместо этого используйте. Просто не забудьте вернуть результат нового get, который станет обещанием, присоединенным к последующим вызовам /* done */fail.

Итак:

$.get('pages/run-tool.html')
.then (function (args) { // this will run if the above .get succeeds
    // This works fine
    alert(args);
    $('#content').replaceWith (args);
    $('#progress-bar').progressbar ({value: 0});
})
.then (function() { // this will run after the above then-handler (assuming it ran)
    return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args) { // this will run after the second .get succeeds (assuming it ran)
    alert (args); 
});

Ответ 2

Все три обратных вызова (два с then и один с done) применяются к одному и тому же запросу - оригинальному вызову when. Это связано с тем, что then возвращает тот же объект отложенного, а не новый, чтобы вы могли добавлять несколько обработчиков событий.

Вместо этого вам нужно использовать pipe.

$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
    // This works fine
    alert(args);
    $('#content').replaceWith (args);
    $('#progress-bar').progressbar ({value: 0});
})
.pipe (function() { 
    return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args)
{
    alert (args);
});

Ответ 3

Вот замечательно простой и высокоэффективный плагин AJAX chaining/queue. Он будет выполнять ваши ajax-методы последовательно один за другим.

Он работает, принимая массив методов, а затем выполняет их последовательно. Он не выполнит следующий метод, ожидая ответа.

//--- ЭТО ЧАСТЬ - ВАШ КОД: -----------------------

$(document).ready(function() {

var AjaxQ = [];
AjaxQ[0] = function () { AjaxMethod1(); }
AjaxQ[1] = function () { AjaxMethod2(); }
AjaxQ[3] = function () { AjaxMethod3(); }

//Execute methods in sequence
$(document).sc_ExecuteAjaxQ({ fx: AjaxQ });

});

//--- ЭТО ЧАСТЬ - ПЛАН. AJAX -------------------

$. fn.sc_ExecuteAjaxQ = function (options) {

//? Executes a series of AJAX methods in dequence

var options = $.extend({

    fx: [] //function1 () { }, function2 () { }, function3 () { }

}, options);

if (options.fx.length > 0) {

    var i = 0;

    $(this).unbind('ajaxComplete');
    $(this).ajaxComplete(function () {

        i++;
        if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); }
        else { $(this).unbind('ajaxComplete'); }

    });

    //Execute first item in queue
    if (typeof options.fx[i] == "function") { options.fx[i](); }
    else { $(this).unbind('ajaxComplete'); }

} 

}

Ответ 4

Ответ cdr дал, который имеет самый высокий голос на данный момент, не прав.

Когда у вас есть функции a, b, c, каждый возвращает объект $.Deferred() и связывает следующие функции:

a().then(b).then(c)

Оба b и c будут выполняться после того, как будет восстановлено обещание, возвращенное из a. Поскольку обе функции then() привязаны к обещанию a, это работает аналогично другим цепочкам Jquery, таким как:

$('#id').html("<div>hello</div>").css({display:"block"})

где вызываются функции html() и css() для объекта, возвращаемого из $('# id');

Итак, чтобы выполнить a, b, c после того, как обещание, возвращенное из предыдущей функции, будет разрешено, вам нужно сделать это:

a().then(function(){
    b().then(c)
});

Здесь вызов функции c связан с обещанием, возвращаемым функцией b.

Вы можете проверить это, используя следующий код:

function a() {
    var promise = $.Deferred();
    setTimeout(function() {
        promise.resolve();
        console.log("a");
    }, 1000);
    return promise;
}

function b() {
    console.log("running b");
    var promise = $.Deferred();
    setTimeout(function () {
        promise.resolve();
        console.log("b");
    }, 500);
    return promise;
}

function c() {
    console.log("running c");
    var promise = $.Deferred();
    setTimeout(function () {
        promise.resolve();
        console.log("c");
    }, 1500);
    return promise;
}

a().then(b).then(c);
a().then(function(){
    b().then(c)
});

Измените обещание в функции b() от resolve() до reject(), и вы увидите разницу.

Ответ 5

<script type="text/javascript">

    var promise1 = function () {
        return new
        $.Deferred(function (def) {
            setTimeout(function () {
                console.log("1");
                def.resolve();
            }, 3000);
        }).promise();
    };

    var promise2 = function () {
        return new
        $.Deferred(function (def) {
            setTimeout(function () {
                console.log("2");
                def.resolve();
            }, 2000);
        }).promise();
    };

    var promise3 = function () {
        return new
        $.Deferred(function (def) {
            setTimeout(function () {
                console.log("3");
                def.resolve();
            }, 1000);
        }).promise();
    };

    var firstCall = function () {
        console.log("firstCall");
        $.when(promise1())
        .then(function () { secondCall(); });
    };

    var secondCall = function () {
        console.log("secondCall")
        $.when(promise2()).then(function () { thirdCall(); });
    };

    var thirdCall = function () {
        console.log("thirdCall")
        $.when(promise3()).then(function () { console.log("done"); });
    };


    $(document).ready(function () {
        firstCall();
    });
</script>

Ответ 6

Я думал, что оставлю это небольшое упражнение здесь для всех, кто может найти это полезным, мы создаем массив запросов, и когда они будут завершены, мы можем запустить функцию обратного вызова:

var urls = [{
    url: 'url1',
    data: 'foo'
}, {
    url: 'url2',
    data: 'foo'
}, {
    url: 'url3',
    data: 'foo'
}, {
    url: 'url4',
    data: 'foo'
}];
var requests = [];
var callback = function (result) {
    console.log('done!');
};

var ajaxFunction = function () {
    for (var request, i = -1; request = urls[++i];) {
        requests.push($.ajax({
            url: request.url,
            success: function (response) {
                console.log('success', response);
            }
        }));
    }
};

// using $.when.apply() we can execute a function when all the requests 
// in the array have completed
$.when.apply(new ajaxFunction(), requests).done(function (result) {
    callback(result)
});

Ответ 7

Мой способ - применить функцию обратного вызова:

A(function(){
       B(function(){
            C()})});

где A, B можно записать в виде

function A(callback)
$.ajax{
    ...
    success: function(result){
        ...
        if (callback) callback();
   }
}