Передайте массив отложенных в $ .when () - PullRequest
422 голосов
/ 12 апреля 2011

Вот надуманный пример того, что происходит: http://jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>

JavaScript:

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

Я хочу "Все готово!"появляется после того, как все отложенные задачи завершены, но $.when(), похоже, не знает, как обрабатывать массив отложенных объектов."Все сделано!"происходит сначала, потому что массив не является отложенным объектом, поэтому jQuery идет вперед и предполагает, что он только что завершен.

Я знаю, что можно передать объекты в функцию, например $.when(deferred1, deferred2, ..., deferredX), но неизвестно, сколько там отложенных объектовбудет при исполнении той самой задачи, которую я пытаюсь решить.

Ответы [ 9 ]

701 голосов
/ 12 апреля 2011

Чтобы передать массив значений в любую функцию , которая обычно ожидает, что они будут отдельными параметрами, используйте Function.prototype.apply, поэтому в этом случае вам нужно:

$.when.apply($, my_array).then( ___ );

См. http://jsfiddle.net/YNGcm/21/

В ES6 вы можете использовать ... оператор распространения вместо:

$.when(...my_array).then( ___ );

В любом случае, так как маловероятно, что вы будете знать взаранее укажите, сколько формальных параметров потребуется обработчику .then, чтобы этот обработчик обработал массив arguments, чтобы получить результат каждого обещания.

102 голосов
/ 25 апреля 2013

Обходные пути, описанные выше (спасибо!), Не решают должным образом проблему возврата объектов, предоставленных методу resolve() отложенного типа, поскольку jQuery вызывает обратные вызовы done() и fail() с отдельными параметрами, а не массивом.Это означает, что мы должны использовать псевдомассив arguments, чтобы получить все разрешенные / отклоненные объекты, возвращаемые массивом deferreds, что выглядит ужасно:

$.when.apply($,deferreds).then(function() {
     var objects=arguments; // The array of resolved objects as a pseudo-array
     ...
};

Поскольку мы передали массив deferreds,было бы неплохо вернуть массив результатов.Было бы также неплохо получить реальный массив вместо псевдомассива, чтобы мы могли использовать такие методы, как Array.sort().

. Вот решение, вдохновленное when.js when.all() метод, который решает эти проблемы:

// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
    jQuery.when.all = function (deferreds) {
        return $.Deferred(function (def) {
            $.when.apply(jQuery, deferreds).then(
                function () {
                    def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                },
                function () {
                    def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                });
        });
    }
}

Теперь вы можете просто передать массив отложенных / обещаний и получить массив разрешенных / отклоненных объектов в вашем обратном вызове, например:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
38 голосов
/ 12 апреля 2011

Вы можете применить метод when к вашему массиву:

var arr = [ /* Deferred objects */ ];

$.when.apply($, arr);

Как вы работаете с массивом отсроченных jQuery?

7 голосов
/ 18 июля 2015

При вызове нескольких параллельных вызовов AJAX у вас есть два варианта обработки соответствующих ответов:

  1. Использовать синхронный вызов AJAX / один за другим / не рекомендуется
  2. Использовать Promises'массив и $.when, который принимает promise s и его обратный вызов .done вызывается, когда все promise s возвращаются успешно с соответствующими ответами.

Пример

function ajaxRequest(capitalCity) {
   return $.ajax({
        url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
        success: function(response) {
        },
        error: function(response) {
          console.log("Error")
        }
    });
}
$(function(){
   var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
   $('#capitals').text(capitalCities);

   function getCountryCapitals(){ //do multiple parallel ajax requests
      var promises = [];   
      for(var i=0,l=capitalCities.length; i<l; i++){
            var promise = ajaxRequest(capitalCities[i]);
            promises.push(promise);
      }
  
      $.when.apply($, promises)
        .done(fillCountryCapitals);
   }
  
   function fillCountryCapitals(){
        var countries = [];
        var responses = arguments;
        for(i in responses){
            console.dir(responses[i]);
            countries.push(responses[i][0][0].nativeName)
        }  
        $('#countries').text(countries);
   }
  
   getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <h4>Capital Cities : </h4> <span id="capitals"></span>
  <h4>Respective Country's Native Names : </h4> <span id="countries"></span>
</div>
5 голосов
/ 08 мая 2015

В качестве простой альтернативы, для которой не требуется $.when.apply или array, вы можете использовать следующий шаблон для генерации одного обещания для нескольких параллельных обещаний:

promise = $.when(promise, anotherPromise);

, например

function GetSomeDeferredStuff() {
    // Start with an empty resolved promise (or undefined does the same!)
    var promise;
    var i = 1;
    for (i = 1; i <= 5; i++) {
        var count = i;

        promise = $.when(promise,
        $.ajax({
            type: "POST",
            url: '/echo/html/',
            data: {
                html: "<p>Task #" + count + " complete.",
                delay: count / 2
            },
            success: function (data) {
                $("div").append(data);
            }
        }));
    }
    return promise;
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            $("div").append("<p>All done!</p>");
        });
    });
});

Примечания:

  • Я понял это после того, как увидел последовательные чьи-то обещания, используя promise = promise.then(newpromise)
  • Недостатком является то, что он создает дополнительные объекты обещания за кулисами, и любые параметры, передаваемые в конце, не очень полезны (так как они вложены в дополнительные объекты). Для того, что вы хотите, хотя это коротко и просто.
  • Плюс в том, что он не требует массива или управления массивами.
4 голосов
/ 22 сентября 2016

Я хочу предложить другой с использованием $ .each:

  1. Мы можем объявить ajax-функцию как:

    function ajaxFn(someData) {
        this.someData = someData;
        var that = this;
        return function () {
            var promise = $.Deferred();
            $.ajax({
                method: "POST",
                url: "url",
                data: that.someData,
                success: function(data) {
                    promise.resolve(data);
                },
                error: function(data) {
                    promise.reject(data);
                }
            })
            return promise;
        }
    }
    
  2. Часть кода, в которой мы создаем массив функций с помощью ajax для отправки:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
    
  3. И вызов функций с отправкой ajax:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
1 голос
/ 20 октября 2016

Если вы перемещаетесь и имеете доступ к ES6, вы можете использовать расширенный синтаксис, который конкретно применяет каждый повторяемый элемент объекта в качестве дискретного аргумента, так же, как это нужно $.when().

$.when(...deferreds).done(() => {
    // do stuff
});

MDN Link - Синтаксис распространения

0 голосов
/ 28 марта 2017

У меня был очень похожий случай, когда я публиковал в каждом цикле, а затем устанавливал html-разметку в некоторых полях из чисел, полученных из ajax.Затем мне нужно было сделать сумму (теперь обновленных) значений этих полей и поместить в общее поле.

Таким образом, проблема заключалась в том, что я пытался сделать сумму для всех чисел, но неданные еще не поступили от асинхронных вызовов AJAX.Мне нужно было завершить эту функцию в несколько функций, чтобы иметь возможность повторно использовать код.Моя внешняя функция ждет данных, прежде чем я пойду и сделаю кое-что с полностью обновленным DOM.

    // 1st
    function Outer() {
        var deferreds = GetAllData();

        $.when.apply($, deferreds).done(function () {
            // now you can do whatever you want with the updated page
        });
    }

    // 2nd
    function GetAllData() {
        var deferreds = [];
        $('.calculatedField').each(function (data) {
            deferreds.push(GetIndividualData($(this)));
        });
        return deferreds;
    }

    // 3rd
    function GetIndividualData(item) {
        var def = new $.Deferred();
        $.post('@Url.Action("GetData")', function (data) {
            item.html(data.valueFromAjax);
            def.resolve(data);
        });
        return def;
    }
0 голосов
/ 21 июля 2014

Если вы используете angularJS или какой-либо вариант библиотеки Q обещаний, то у вас есть метод .all(), который решает именно эту проблему.

var savePromises = [];
angular.forEach(models, function(model){
  savePromises.push(
    model.saveToServer()
  )
});

$q.all(savePromises).then(
  function success(results){...},
  function failed(results){...}
);

см. Полный API:

https://github.com/kriskowal/q/wiki/API-Reference#promiseall

https://docs.angularjs.org/api/ng/service/$q

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...