Отложенный объект возвращается до того, как он будет отклонен или разрешен?

У меня есть функция, которая в основном выглядит так:

function defTest()
{
    var dfd = new jQuery.Deferred();
    $.ajax(
    {
        type: "GET",
        url: 'http://XXXX',
        cache: false,
        dataType: "json",
        success: function(data,status)
        {
            console.log('ajax done: success');
            dfd.resolve();
        },
        error: function(data,status)
        {
            console.log('ajax done: fail');
            dfd.reject();

        }
    });
    console.log('about to return dfd');
    return dfd;    
}

Я называю это так:

defTest().then(.....);

Журнал консоли выдает следующее: собирается вернуть dfd code.js:106 ajax done: success code.js:96

Что меня смущает, так это то, что код работает. Тем не менее dfd возвращается ДО завершения ajax. Поэтому я удалил возврат dfd. И поместите его последним в каждую функцию обработчика ajax, чтобы убедиться, что это НЕ будет возвращено, пока Ajax не завершится.:

success: function(data,status)
{
    console.log('ajax done: success');
    dfd.resolve();
    return dfd;
}

Тогда это вообще не работало. Я смущен! Может ли кто-нибудь объяснить мне, почему мой отложенный объект не может быть в обработчиках успеха и ошибок ajax и почему он работает, даже если кажется, что мой отложенный объект возвращается, даже если он запускается ДО завершения Ajax, а затем разрешается или отклоняется? Как это вообще возможно?

РЕДАКТИРОВАТЬ: Эта проблема напрямую связана с моей предыдущей неотвеченной и более сложной функцией: Проблемы с отложенным объектом

Вот почему я не могу просто «вернуть ajax (...)», потому что моя реальная функция содержит другие вызовы ajax, которые будут отделены от ОДНОГО результата, возвращаемого вызывающей стороне.


person Juw    schedule 12.09.2012    source источник
comment
проблема XY. Вы не можете вернуть результат асинхронного действия из своей функции, который похож на то, что вы пытаетесь сделать.   -  person Fabrício Matté    schedule 12.09.2012
comment
Это то, что я пытаюсь решить, используя отложенные объекты.   -  person Juw    schedule 12.09.2012
comment
$.ajax уже возвращает отложенный объект, как ответил xdazz. JavaScript является однопоточным, вызовы функций синхронны и должны возвращать значение (или ничего) синхронно. $.ajax является асинхронным. Что вы можете (и должны) сделать, так это использовать обратный вызов успеха ajax, чтобы сделать то, что вам нужно сделать, когда запрос ajax будет завершен.   -  person Fabrício Matté    schedule 12.09.2012
comment
@Fabricio, спасибо за ваш комментарий. Но я не могу просто вернуть ajax(), так как это не решит мою проблему... взгляните на часть, которую я отредактировал в своем посте.   -  person Juw    schedule 12.09.2012


Ответы (5)


$.ajax возвращает объект Deferred, поэтому вам нужно только вернуть его.

return $.ajax(...

person xdazz    schedule 12.09.2012
comment
Да, я знаю это. Но мне нужно справиться с этим самой. Функция намного сложнее в моем реальном коде (множественный вызов ajax). Мне нужно знать, почему мой код ведет себя так. - person Juw; 12.09.2012

Ваш вызов defTest вернется, как только ваш вызов ajax будет запущен. Он не будет ждать, пока deferred будет разрешен или нет.

Но вы вполне можете делать то, что хотите:

defTest().then(function() { console.log("deferred done"); })

Он напечатает deferred done, когда deferred будет окончательно разрешен.

Кстати, как предложил xdazz, $.ajax уже возвращает Deferred.

person Guillaume Poussel    schedule 12.09.2012
comment
Да, я знаю это. Но моя проблема немного сложнее, чем эта функция (но та же проблема), посмотрите на мой пост без ответа: stackoverflow.com/questions/12365568/ - person Juw; 12.09.2012

есть много подобных вопросов
AJAX запрос по своей природе является async, поэтому в первом примере, когда вы возвращали его в последней строке.
не нужно ждать завершения async ajax
а во втором случае функция уже завершена, и успешное завершение возврата, очевидно, ничего не вернет, поскольку вы не вызываете метод, назначенный событию success, напрямую
ОБНОВЛЕНИЕ:- после комментария
потому что после возврата deffered объект, который вы выполняете defTest().then(.....);
, поэтому функция внутри обработчика then, очевидно, будет запущена, когда будет разрешен объект deffered.
возможно, вам следует проверить значение объекта deffered, как только вы его вернете, а не использовать .then() и вы поймете, какую ошибку вы делаете здесь
Обновление 2 -
посмотрите здесь http://jsfiddle.net/BtEKa/ я получаю предсказуемые результаты

person Parv Sharma    schedule 12.09.2012
comment
Но в моем первом случае, кажется, работает возврат разрешенного или отклоненного объекта. Потому что даже если console.log кажется не в порядке. Каждый раз получаю идеальные результаты. Как это возможно? - person Juw; 12.09.2012
comment
Спасибо, Парв Шарма. Но это работает. Даже если журнал говорит что-то другое. использование возврата dfd работает в конце. Будет инициировано событие успеха или неудачи вызывающего абонента. Но как это возможно, поскольку кажется (согласно console.log, что return dfd запускается ДО того, как фактический статус ajax завершится и заставит dfd разрешить или отклонить. Как это возможно? Логика должна заключаться в том, что dfd вернет статус ожидания, так как он возвращается ДО того, как он был отклонен или решен.Это меня немного пугает, так как я не понимаю, как это может быть. - person Juw; 12.09.2012
comment
Взгляните на это: jsfiddle.net/BtEKa/9 Думаю, потому что я использую alert() у ajax есть время для завершения или что-то в этом роде. Итак, вернемся к шагу 1. Как мне убедиться, что я получу успешный или неудачный результат для вызывающей стороны на основе функции? - person Juw; 12.09.2012
comment
Эта проблема была бы решена, если бы вы могли сделать это следующим образом: jsfiddle.net/2GVbA/1 Но вы не можете запустить return dfd; по какой-то причине. - person Juw; 12.09.2012

Конечно вернули сразу, в том-то и дело. Однако, как правило, можно было бы вернуть член обещания отложенного объекта -- хотя вы могли бы вернуть отложенный объект и использовать returnDeferredObject.promise().then() в коде, который вызывает эту функцию.

Если у вас есть обработка, зависящая от завершения AJAX, эта обработка выполняется в функции .then() возвращаемого .promise(). Прелесть в том, что пока идет асинхронная обработка, вы можете делать другие вещи, которые не зависят от возврата AJAX. Вы можете передать возвращенный .promise() другому коду.

var mydata = somethingThatReturnsPromise(args);

// do a whole bunch of things

mydata.then(function (returnedReponse) {
    // do stuff with returnedResponse that doesn't require DOM ready
});

$(function () {
    mydata.then(function (returnedResponse) {
        // do DOM stuff with returnedPromise
    });
});

Я надеюсь, что не упустил вашу мысль, но мне нравится думать об этом возвращенном обещании как об источнике данных, который я могу использовать снова и снова позже, и обратные вызовы .then(), которые я указываю, будут выполняться только после того, как будет возвращен ответ. .

В качестве примечания: я почти уверен, что, поскольку jQuery приближается к стандарту обещаний, .done(), .pipe(), .fail() и .progress() следует заменить на использование .then(successCb, failCb , прогрессCb).

person grantwparks    schedule 24.03.2013

Я считаю, что ваша проблема в том, что вы возвращаете весь отложенный объект, а не обещание этого объекта.

Промис, afiak, всегда возвращается до того, как будет возвращен асинхронный вызов, поэтому, хотя ваш последний console.log сработает до того, как будет возвращен PROMISE, это произойдет задолго до того, как ajax будет разрешен.

использование .then() также, вероятно, не то, что вам нужно, поскольку оно срабатывает независимо от статуса отложенного объекта: http://api.jquery.com/deferred.then/

Вы, вероятно, хотите .done()

Попробуй это:

function defTest()
{
    var dfd = $.Deferred();
    $.getJSON("http://XXXX").done(function(data,success){
      console.log('ajax done: success');
      dfd.resolve(data);
    }).fail( function(data,status) {
      console.log('ajax done: fail');
      dfd.reject();
    });
    console.log('about to return dfds promise');
    return dfd.promise;    
}
defTest().done(function(data){ console.log(data); });
person Dana McIntosh    schedule 17.01.2013