Promise.all: ordem dos valores resolvidos


189

Observando o MDN , parece que o valuespassado para o then()retorno de chamada de Promise.all contém os valores na ordem das promessas. Por exemplo:

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

Alguém pode citar uma especificação informando em que ordem valuesdeve estar?

PS: Executar um código como esse mostrou que isso parece verdade, embora isso não seja prova - poderia ter sido coincidência.

Respostas:


274

Logo, o pedido é preservado .

Após a especificação à qual você se vinculou, Promise.all(iterable)leva um iterable(ou seja, um objeto que suporta a Iteratorinterface) como parâmetro e, posteriormente, faz uma chamada PerformPromiseAll( iterator, constructor, resultCapability)com ele, onde o último faz iterableuso repetido IteratorStep(iterator).
Isso significa que, se o iterável para o qual você entra Promise.all()for estritamente ordenado, ele ainda será solicitado assim que for passado.

A resolução é implementada através do Promise.all() Resolvelocal em que cada promessa resolvida possui um [[Index]]slot interno , que marca o índice da promessa na entrada original.


Tudo isso significa que a saída é estritamente ordenada como entrada, desde que estritamente ordenada (por exemplo, uma matriz).

Você pode ver isso em ação no violino abaixo (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));
});


1
Como um iterável não seria estritamente ordenado? Qualquer iterável é "estritamente ordenado" pela ordem em que produz seus valores.
— Benjamin Gruenbaum

Nota - O Firefox é o único navegador que implementa iterables nas promessas corretamente. No momento, o Chrome será throwuma exceção se você passar um iterável para Promise.all. Além disso, não estou ciente de nenhuma implementação de promessa de usuário que atualmente ofereça suporte a iterables, embora muitos tenham debatido e decidido contra isso na época.
— Benjamin Gruenbaum

3
@BenjaminGruenbaum Não é possível ter um iterável que produz duas ordens diferentes ao ser iterado duas vezes? Por exemplo, um baralho de cartas que produz cartas em ordem aleatória quando é iterado? Não sei se "estritamente ordenada" é a terminologia correta aqui, mas nem todas as iteráveis ​​têm uma ordem fixa. Portanto, acho razoável dizer que os iteradores são "estritamente ordenados" (supondo que esse seja o termo certo), mas os iteráveis não.
— JLRishe

3
@JLRishe Acho que você está certo, são de fato os iteradores que são pedidos - os iteráveis ​​não.
— Benjamin Gruenbaum

8
Vale a pena notar que as promessas não acorrentam. Embora você obtenha a resolução na mesma ordem, não há garantia de quando as promessas são cumpridas. Em outras palavras, Promise.allnão pode ser usado para executar uma série de promessas em ordem, uma após a outra. As promessas carregadas no iterador precisam ser independentes uma da outra para que isso funcione previsivelmente.
— Andrew Eddie

49

Como as respostas anteriores já declararam, Promise.allagrega todos os valores resolvidos com uma matriz correspondente à ordem de entrada das promessas originais (consulte Agregando promessas ).

No entanto, gostaria de salientar que o pedido é preservado apenas no lado do cliente!

Para o desenvolvedor, parece que as promessas foram cumpridas em ordem, mas, na realidade, as promessas são processadas em velocidades diferentes. É importante saber quando você trabalha com um back-end remoto, pois o back-end pode receber suas promessas em uma ordem diferente.

Aqui está um exemplo que demonstra o problema usando tempos limite:

Promise.all

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)

No código mostrado acima, três promessas (A, B, C) são dadas Promise.all. As três promessas são executadas em velocidades diferentes (C sendo a mais rápida e B sendo a mais lenta). É por isso que as console.logdeclarações das promessas aparecem nesta ordem:

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

Se as Promessas forem chamadas AJAX, um back-end remoto receberá esses valores nesta ordem. Mas no lado do cliente Promise.allgarante que os resultados sejam ordenados de acordo com as posições originais da myPromisesmatriz. É por isso que o resultado final é:

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

Se você deseja garantir também a execução real de suas promessas, precisará de um conceito como uma fila de promessas. Aqui está um exemplo usando a fila p (tenha cuidado, você precisa agrupar todas as promessas em funções):

Fila de Promessa Sequencial

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

Resultado

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

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

2
grande resposta, expecially usando PQueue
— ironstein

Preciso de uma fila seqüencial de promessas, mas como fazer isso se for necessário fazê-lo a partir de um resultado de registros sql? em um para? while?, nenhuma alternativa no ES2017 nosso ES2018?
— stackdave

PQueue me ajudou! Obrigado! :)
— podeig 02/07

28

Sim, os valores em resultsestão na mesma ordem que o promises.

Pode-se citar a especificaçãoPromise.all do ES6 , embora seja um pouco complicada devido à API do iterador usada e ao construtor de promessa genérico. No entanto, você notará que cada retorno de chamada do resolvedor possui um [[index]]atributo que é criado na iteração da matriz de promessas e usado para definir os valores na matriz de resultados.


Estranho, vi hoje um vídeo do youtube que dizia que a ordem de saída é determinada pelo primeiro que resolveu, depois pelo segundo e depois ... Acho que o OP do vídeo estava errado?
— Royi Namir

1
@RoyiNamir: Aparentemente ele era.
— 21415 Bergi

@Ozil Wat? A ordem cronológica da resolução não importa absolutamente quando todas as promessas são cumpridas. A ordem dos valores na matriz de resultados é a mesma que na matriz de entrada de promessas. Caso contrário, mude para uma implementação de promessa adequada.
— Bergi 21/09/17
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.