Promise.all: Orden de los valores resueltos

Resuelto Thorben Croisé asked hace 9 años • 3 respuestas

Mirando MDN, parece que la valuesdevolución de then()llamada de Promise.all pasada contiene los valores en el orden de las promesas. Por ejemplo:

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

¿Alguien puede citar una especificación que indique en qué orden valuesdebe estar?

PD: La ejecución de un código como ese demostró que esto parece ser cierto aunque, por supuesto, eso no es prueba: podría haber sido una coincidencia.

Thorben Croisé avatar Jan 21 '15 18:01 Thorben Croisé
Aceptado

En breve se mantiene el orden .

Siguiendo la especificación a la que se vinculó, Promise.all(iterable)toma un iterablecomo parámetro y PerformPromiseAll(iterator, constructor, resultCapability)lo llama internamente, donde este último se repite iterableusando IteratorStep(iterator).

La resolución se implementa a través de Promise.all() Resolvedonde cada promesa resuelta tiene una [[Index]]ranura interna, que marca el índice de la promesa en la entrada original.


Todo esto significa que la salida está estrictamente ordenada dado que el iterable que pasa a Promise.all() está estrictamente ordenado (por ejemplo, una matriz).

Puedes ver esto en acción en el siguiente violín (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));
});
Expandir fragmento

Etheryte avatar Jan 21 '2015 12:01 Etheryte

Como ya se indicó en las respuestas anteriores, Promise.allagrega todos los valores resueltos con una matriz correspondiente al orden de entrada de las Promesas originales (consulte Agregación de Promesas ).

Sin embargo, me gustaría señalar que el pedido sólo se conserva en el lado del cliente.

Para el desarrollador, parece que las Promesas se cumplieron en orden, pero en realidad, las Promesas se procesan a diferentes velocidades. Es importante saber esto cuando trabaja con un backend remoto porque el backend puede recibir sus Promesas en un orden diferente.

A continuación se muestra un ejemplo que demuestra el problema mediante el uso de tiempos de espera:

Promesa.todo

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)
Expandir fragmento

En el código que se muestra arriba, se dan tres Promesas (A, B, C) Promise.all. Las tres Promesas se ejecutan a diferentes velocidades (siendo C la más rápida y B la más lenta). Por eso las console.logdeclaraciones de las Promesas aparecen en este orden:

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

Si las Promesas son llamadas AJAX, entonces un backend remoto recibirá estos valores en este orden. Pero del lado del cliente Promise.allse asegura que los resultados estén ordenados según las posiciones originales de la myPromisesmatriz. Por eso el resultado final es:

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

Si desea garantizar también la ejecución real de sus Promesas, entonces necesitaría un concepto como una cola de Promesas. A continuación se muestra un ejemplo que utiliza p-queue (tenga cuidado, debe envolver todas las promesas en funciones):

Cola de promesa secuencial

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)']
Benny Code avatar Feb 15 '2018 17:02 Benny Code