2015-01-21 2 views
84

Посмотрите на MDN. Похоже, что values, переданный в then(), обратный вызов Promise.all содержит значения в порядке выполнения обещаний. Например:Promise.all: Порядок разрешенных значений

var somePromises = [1, 2, 3, 4, 5].map(Promise.resolve); 
return Promise.all(somePromises).then(function(results) { 
    console.log(results) // is [1, 2, 3, 4, 5] the guaranteed result? 
}); 

Может кто-нибудь процитировать спецификации о том, в каком порядке values должен быть?

PS: Запуск такого кода показал, что это кажется правдой, хотя это, конечно, не доказательство - это могло быть совпадением.

ответ

117

Вскоре заказ сохраняется.

После спецификации вы связаны, Promise.all(iterable) принимает iterable (то есть объект, который поддерживает интерфейс Iterator) в качестве параметра, а затем на звонки PerformPromiseAll(iterator, constructor, resultCapability) с ним, где последние петли над iterable использованием IteratorStep(iterator).
Это означает, что если если итерация вы передаете к Promise.all() строго упорядочена, они все равно будут упорядоченные когда-то проходил в.

Решение реализуется с помощью Promise.all() Resolve где каждый решено обещание имеет внутренний [[Index]] слот, который помечает индекс обещания в оригинальном вводе.


Все это означает, что выходной сигнал строго заказан как вход до тех пор, пока вход строго упорядоченные (например, массив).

Вы можете увидеть это в действии в следующей скрипке (ES6):

// Used to display results 
 
const write = msg => { 
 
    document.body.appendChild(document.createElement('div')).innerHTML = msg; 
 
}; 
 

 
// Different speed async operations 
 
const slow = new Promise(resolve => { 
 
    setTimeout(resolve, 200, 'slow'); 
 
}); 
 
const instant = 'instant'; 
 
const quick = new Promise(resolve => { 
 
    setTimeout(resolve, 50, 'quick'); 
 
}); 
 

 
// The order is preserved regardless of what resolved first 
 
Promise.all([slow, instant, quick]).then(responses => { 
 
    responses.map(response => write(response)); 
 
});

+0

Как можно было бы строго упорядочить итерацию? Любой итерабельный «строго упорядочен» по порядку, в котором он производит свои значения. –

+0

Примечание. Firefox является единственным браузером, который правильно выполняет итерации в обещаниях. В настоящее время Chrome будет «бросать» excpetion, если вы передадите итерабельному «Promise.all». Кроме того, мне не известно о реализации пользовательских обещаний, которые в настоящее время поддерживают передачу итераций, хотя многие из них обсуждали это и решили против этого в то время. –

+0

@BenjaminGruenbaum 'Object' не имеет поведения итерации по умолчанию (потому что он не строго упорядочен), что означает, что если вы используете его вместе с предоставлением ему итерационного интерфейса, порядок может быть неоднозначным в зависимости от реализации. – Nit

19

Да, значения в results находятся в том же порядке, что и у promises.

Можно было бы назвать ES6 spec on Promise.all, хотя он немного запутан из-за использования итератора api и универсального конструктора обещаний. Тем не менее, вы заметите, что каждый обратный вызов resolver имеет атрибут [[index]], который создается в итерации итерации с обещаниями и используется для установки значений в массиве результатов.

+2

На самом деле не знаю, почему это была приостановлена. –

+0

Странно, сегодня я видел видео с YouTube, в котором говорилось, что порядок вывода определяется первым, кто решил, затем вторым, тогда ..... Я думаю, что видео OP было неправильным? –

+1

@RoyiNamir: Видимо, он был. – Bergi

1

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

Однако я хотел бы указать, что заказ сохраняется только на стороне клиента!

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

Ниже приведен пример, который демонстрирует проблему с помощью тайм-аута:

обещания.все

const myPromises = [ 
 
    new Promise((resolve) => setTimeout(() => {resolve('A (slow)'); console.log('A (slow)')}, 1000)), 
 
    new Promise((resolve) => setTimeout(() => {resolve('B (slower)'); console.log('B (slower)')}, 2000)), 
 
    new Promise((resolve) => setTimeout(() => {resolve('C (fast)'); console.log('C (fast)')}, 10)) 
 
]; 
 

 
Promise.all(myPromises).then(console.log)

В коде показано выше, три Обещания (A, B, C), приведены в Promise.all. Три Promises выполняются с разной скоростью (C является самым быстрым, а B - самым медленным). Вот почему console.log высказывания обетований показывают в следующем порядке:

C (fast) 
A (slow) 
B (slower) 

Если Обещания AJAX вызовов, то удаленный бэкенд получит эти значения в указанном порядке. Но на стороне клиента Promise.all гарантирует, что результаты упорядочены в соответствии с исходными позициями массива myPromises. Вот почему конечный результат:

['A (slow)', 'B (slower)', 'C (fast)'] 

Если вы хотите, чтобы гарантировать также фактическое исполнение своих обещаний, то вам нужно будет такое понятие, как очереди Promise. Ниже приведен пример использования p-queue (будьте осторожны, вам нужно обернуть все посылы в функции):

Последовательная Promise Очередь

const PQueue = require('p-queue'); 
const queue = new PQueue({concurrency: 1}); 

// Thunked Promises: 
const myPromises = [ 
() => new Promise((resolve) => setTimeout(() => { 
    resolve('A (slow)'); 
    console.log('A (slow)'); 
    }, 1000)), 
() => new Promise((resolve) => setTimeout(() => { 
    resolve('B (slower)'); 
    console.log('B (slower)'); 
    }, 2000)), 
() => new Promise((resolve) => setTimeout(() => { 
    resolve('C (fast)'); 
    console.log('C (fast)'); 
    }, 10)) 
]; 

queue.addAll(myPromises).then(console.log); 

Результат

A (slow) 
B (slower) 
C (fast) 

['A (slow)', 'B (slower)', 'C (fast)'] 
+1

отличный ответ, особенно используя PQueue – ironstein

Смежные вопросы