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

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

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

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

var promiseOfArray = someTransform(arrayOfPromises)

где

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

создает предупреждение с текстом

результат1, результат2, result3, result4

В настоящее время я определяю 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 для достижения того же результата?

Ответы

Ответ 1

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

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

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

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

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

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

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

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

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

Если вы действительно хотите присоединиться к строковому результату всех ваших отсрочек, мы можем использовать небольшой хакерский хакер. 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);
    });
};

Ответ 2

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

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

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

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

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