Каков самый чистый способ превратить массив обещаний JQuery в обещание массива JQuery?

Я сталкиваюсь с ситуацией, когда у меня есть несколько промисов JQuery в массиве.

var arrayOfPromises = [ $.Deferred(), $.Deferred(), $.Deferred(), $.Deferred() ]

и нужно превратить его в обещание массива JQuery

var promiseOfArray = someTransform(arrayOfPromises)

куда

promiseOfArray.done(function(anArray){
  alert(anArray.join(","));
});

создает оповещение с текстом

результат1, результат2, результат3, результат4

В настоящее время я определяю someTransform в coffeescript как

someTransform = (arrayOfPromises) ->
  $.when(arrayOfPromises...).pipe (promises...) ->
    promises

который преобразуется в следующий javascript

var someTransform,
  __slice = [].slice;

someTransform = function(arrayOfPromises) {
  return $.when.apply($, arrayOfPromises).pipe(function() {
    var promises;
    promises = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
    return promises;
  });
};

Вот jsFiddle результата, который я ищу.

Мне было интересно, есть ли лучший (более короткий, более чистый) способ определить someTransform для достижения того же результата?


person Bobby    schedule 30.08.2012    source источник
comment
возможный дубликат Передать массив Deferreds в $.when()   -  person Bergi    schedule 30.03.2016


Ответы (2)


Вы можете просто применить массив в качестве аргументов до $.when.

var promiseOfArray = $.when.apply($, arrayOfPromises);

Чтобы сделать использование этого более понятным, мне нравится добавлять метод к $:

$.whenall = function(arr) { return $.when.apply($, arr); };

Теперь вы можете сделать:

$.whenall([deferred1, deferred2, ...]).done(...);

Обновление. По умолчанию обработчик done получает каждый результат в виде отдельного аргумента; вы не получаете массив результатов.

Поскольку вам нужно обрабатывать произвольное количество Deferred'ов, вы можете использовать специальный неявный arguments объект для циклического просмотра результатов.

$.whenall([d1, d2, ...]).done(function() {
    for (var i = 0; i < arguments.length; i++) {
        // do something with arguments[i]
    }
});

Если вы действительно просто хотите соединить строковый результат всех ваших Deferred'ов, мы можем применить небольшой хакерский прием. arguments похож на массив, но не является Array:

$.whenall([d1, d2, ...]).done(function() {
    alert(Array.prototype.join.call(arguments, ','));
});

Если вы хотите вернуть массив результатов обратному вызову done, мы можем настроить whenall для этого:

$.whenall = function(arr) {
    return $.when.apply($, arr).pipe(function() {
        return Array.prototype.slice.call(arguments);
    });
};
person josh3736    schedule 30.08.2012
comment
Я думаю, что ОП на самом деле запрашивает более короткий и чистый способ и уже знает о .apply() - person jAndy; 31.08.2012
comment
Я проясню вопрос, но я считаю, что это будет выполнено с помощью result1,result2,..., но я хочу, чтобы это было выполнено с одним параметром [result1,result2,...], см. jsfiddle.net/qk2mT/2 выдает, что объект 0 не имеет метода "присоединиться" - person Bobby; 31.08.2012
comment
@Soldier.moth: обработчик done не получает массив результатов; скорее, ему передаются аргументы n, где n — это количество отложенных вызовов, которые вы передали изначально. Используйте неявный объект arguments для просмотра результатов в цикле. - person josh3736; 31.08.2012
comment
@ josh3736: Да, я надеялся, что это хороший метод для преобразования arguments в массив для меня. Может быть выполнено с помощью определенных $.whenall, таких как function(arr) { return $.when.apply($,arr).pipe(function(){ return [].slice.call(arguments,0); }); } - person Bobby; 31.08.2012
comment
@Soldier.moth: Да, это сработает. Я бы использовал Array.prototype.slice вместо [], чтобы вы не создавали экземпляр Array без необходимости. Смотрите обновление. - person josh3736; 31.08.2012

Меня также очень беспокоило то, что я всегда вводил «уродливую» строку $.when.apply, когда нам нужно вызывать ее для нескольких промисов. Но Function.prototype.bind на помощь!

var when = Function.prototype.apply.bind( jQuery.when, null );

Теперь мы можем просто позвонить

when( someArrayWithPromises ).done(function() {
});

Function.prototype.bind является частью ES5 и широко доступен во всех браузерах. Существует множество простых прокладок, если вам нужна поддержка очень старых браузеров

person jAndy    schedule 30.08.2012
comment
Вы должны быть осторожны с bind, так как он недоступен в старые браузеры. - person josh3736; 31.08.2012