Przekazać w tablicy Deferreds do $.kiedy()

Oto wymyślony przykład tego, co się dzieje: 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>");
        });
    });
});

Chcę " wszystko gotowe!"pojawia się po wykonaniu wszystkich zadań odroczonych, ale $.when() nie zdaje się wiedzieć, jak obsługiwać tablicę obiektów odroczonych. "Gotowe!"dzieje się najpierw, ponieważ tablica nie jest obiektem odroczonym, więc jQuery idzie naprzód i zakłada, że jest po prostu zrobione.

Wiem, że można przejść przez obiekty do funkcji jak $.when(deferred1, deferred2, ..., deferredX), ale nie wiadomo, ile obiektów odroczonych będzie w trakcie wykonywania w rzeczywistym problemie, który próbuję rozwiązać.

Author: Alnitak, 2011-04-12

9 answers

Aby przekazać tablicę wartości do dowolnej funkcji , która normalnie oczekuje, że będą oddzielnymi parametrami, użyj Function.prototype.apply, więc w tym przypadku potrzebujesz:

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

Zobacz http://jsfiddle.net/YNGcm/21/

W ES6 możesz użyć ... operator spreadu zamiast:

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

W obu przypadkach, ponieważ jest mało prawdopodobne, abyś wiedział z góry, ile formalnych parametrów będzie wymagał .then, ten handler musi przetworzyć tablicę arguments, aby Odzyskaj wynik każdej obietnicy.

 670
Author: Alnitak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-06 16:50:23

Obejścia powyżej (dzięki!) nie rozwiązuje problemu odzyskania obiektów dostarczonych do metody resolve() deferred, ponieważ jQuery wywołuje wywołania zwrotne done() i fail() z indywidualnymi parametrami, a nie tablicą. Oznacza to, że musimy użyć pseudo-tablicy arguments, aby uzyskać wszystkie rozwiązane / odrzucone obiekty zwrócone przez tablicę deferredów, co jest brzydkie:

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

Ponieważ przeszliśmy w tablicy odroczeń, byłoby miło odzyskać tablicę wyników. Byłoby byłoby też miło odzyskać rzeczywistą tablicę zamiast pseudo-tablicy, abyśmy mogli używać metod takich jak Array.sort().

Oto rozwiązanie inspirowane kiedy.metoda js'S when.all(), która rozwiązuje te problemy:

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

Teraz możesz po prostu przekazać tablicę deferreds / promises i odzyskać tablicę rozwiązanych / odrzuconych obiektów w Twoim wywołaniu zwrotnym, tak:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
 98
Author: crispyduck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-20 12:47:59

Możesz zastosować metodę when do tablicy:

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

$.when.apply($, arr);

Jak pracować z tablicą jQuery?

 38
Author: Eli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:36

Podczas wywoływania wielu równoległych wywołań AJAX, masz dwie opcje obsługi odpowiednich odpowiedzi.

  1. Użyj synchronicznego wywołania AJAX / jedno po drugim/ nie zalecane
  2. Użyj Promises' tablicy i $.when który akceptuje promise s i jego wywołanie zwrotne .donezostanie wywołane, gdy wszystkie promise s powrócą pomyślnie z odpowiednimi odpowiedziami.

Przykład

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
Author: vinayakj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-18 22:03:56

Jako prosta alternatywa, która nie wymaga $.when.apply ani array, możesz użyć następującego wzoru do wygenerowania pojedynczej obietnicy dla wielu równoległych obietnic:

promise = $.when(promise, anotherPromise);

Np.

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

Uwagi:

  • domyśliłem się tego po zobaczeniu kogoś łańcuch obietnic sekwencyjnie, używając promise = promise.then(newpromise)
  • minusem jest to, że tworzy dodatkowe obiekty obietnicy za kulisami, a wszelkie parametry przekazywane na końcu nie są zbyt użyteczne (ponieważ są zagnieżdżone wewnątrz dodatkowych obiektów obiektów). Za to, co chcesz, choć jest krótki i prosty.
  • plusem jest to, że nie wymaga zarządzania tablicami ani tablicami.
 5
Author: Gone Coding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-12 10:20:12

Chcę zaproponować inny z użyciem $."każdy": {]}

  1. Możemy zadeklarować funkcję ajax w postaci:

    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. Część kodu, w której tworzymy tablicę funkcji z Ajaxem do wysłania:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
    
  3. I wywołanie funkcji z wysłaniem ajax:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
 3
Author: Volodymyr Yasinskyi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 06:51:11

Jeśli transpilujesz i masz dostęp do ES6, możesz użyć składni spread, która specjalnie stosuje każdy iterowalny element obiektu jako dyskretny argument, dokładnie tak, jak potrzebuje tego $.when().

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

Składnia MDN Link-Spread

 1
Author: relic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-19 21:05:31

Jeśli używasz angularJS lub jakiegoś wariantu Biblioteki Q promise, to masz metodę .all(), która rozwiązuje dokładnie ten problem.

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

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

Zobacz pełne API:

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

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

 0
Author: mastaBlasta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-01 04:51:15

Miałem bardzo podobny przypadek, w którym umieszczałem w każdej pętli, a następnie ustawiałem znaczniki html w niektórych polach z liczb otrzymanych z ajax. Następnie musiałem zrobić sumę (teraz zaktualizowanych) wartości tych pól i umieścić w całkowitym polu.

Tak więc problem polegał na tym, że próbowałem zrobić sumę na wszystkich numerach, ale żadne dane nie wróciły jeszcze z połączeń async ajax. Musiałem uzupełnić tę funkcjonalność w kilku funkcjach, aby móc ponownie użyć kodu. Moje zewnętrzne funkcja czeka na dane, zanim pójdę i zrobię kilka rzeczy z w pełni zaktualizowanym 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
Author: Cameron Forward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-28 10:22:13