Promise.all: Orden de los valores resueltos
Mirando MDN, parece que la values
devolució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 values
debe 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.
En breve se mantiene el orden .
Siguiendo la especificación a la que se vinculó, Promise.all(iterable)
toma un iterable
como parámetro y PerformPromiseAll(iterator, constructor, resultCapability)
lo llama internamente, donde este último se repite iterable
usando IteratorStep(iterator)
.
La resolución se implementa a través de Promise.all() Resolve
donde 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));
});
Como ya se indicó en las respuestas anteriores, Promise.all
agrega 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)
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.log
declaraciones 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.all
se asegura que los resultados estén ordenados según las posiciones originales de la myPromises
matriz. 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)']