javascript - then - Realice acciones a medida que las promesas se cumplan con Promise.all()
promises javascript w3schools (4)
La respuesta de nem035 es acertada. Quiero señalar que normalmente, en este caso, desea realizar la misma acción cuando se producen las solicitudes y otra acción cuando se hayan completado.
Puedes usar .all
para eso con .map
:
Promise.all([ getStuff(1, 200),
getStuff(2, 100),
getStuff(3, 250),
getStuff(4, 200),
getStuff(5, 300),
getStuff(6, 250),
getStuff(7, 5000)]
.map(request => request.then(v => {
console.log("Request done! Got," v); // or some action per request
return v;
})).then(data => console.log(data));
Puede hacer esto aún más con .map
usando el hecho de que está usando la misma función para cada solicitud:
Promise.all([[1, 200],
[2, 100],
[3, 250],
[4, 200],
[5, 300],
[6, 250],
[7, 5000]])
.map((a, b) => getStuff(a, b))
.map(request => request.then(v => {
console.log("Request done! Got," v); // or some action per request
return v;
})).then(data => console.log(data));
Y además de:
Promise.all([200, 100, 250, 200, 300, 250, 5000])
.map((a, i) => getStuff(a, i + 1))
.map(request => request.then(v => {
console.log("Request done! Got," v); // or some action per request
return v;
})).then(data => console.log(data));
O con bluebird:
const sideEffect = v => console.log("Got partial result", v));
const data = [200, 100, 250, 200, 300, 250, 5000];
Promise.map(data, (a, i) => getStuff(a, i + 1).tap(sideEffect))
.then(data => console.log(data));
Por supuesto, solo debe arreglar su backend, es totalmente irrazonable pedirle al cliente que haga 7 solicitudes de diferentes partes de los datos, tener los rangos de toma de backend.
Puedo resolver asincrónicamente un montón de promesas con Promise.all(array)
. Sin embargo .then()
se ejecutará solo después de que se hayan resuelto todas esas promesas. ¿Cómo puedo realizar acciones mientras se resuelven las promesas?
Por ejemplo, quiero cargar todos los párrafos de un artículo de forma asincrónica utilizando Promise.all()
: de esta manera, la red solicita que se Promise.all()
todos a la vez. Si el párrafo 1 termina de cargarse, quiero que se muestre en la página, pero solo si se realiza antes del párrafo 2, quiero que se cargue el párrafo 2. Si el párrafo 3 termina de cargarse y 2 no, quiero que 3 espere 2 antes de renderizar a la página. Y así.
Intenté algo como esto pero no sé qué hacer a continuación:
var getStuff = function(number, time){
return new Promise(function(resolve, reject){
window.setTimeout(function(){resolve(`${number} - Done.`)}, time);
});
};
Promise.all([ getStuff(1, 200),
getStuff(2, 100),
getStuff(3, 250),
getStuff(4, 200),
getStuff(5, 300),
getStuff(6, 250),
getStuff(7, 5000)])
.then(function(data){
console.log(data);
});
¿Cómo puedo hacer que el registro de la consola de los datos suceda uno tras otro, sin resolver cada promesa con un then()
antes de realizar la siguiente solicitud? ¿Hay una mejor manera de hacer esto?
No puede lograr este orden utilizando Promise.all
porque la promesa devuelta desde Promise.all
espera que todas las promesas de la matriz provista se resuelvan simultáneamente (en lugar de secuencialmente) antes de que se resuelva.
En su lugar, puede crear individualmente las promesas y el envío de sus solicitudes al mismo tiempo:
// create promises and make concurrent requests
const s1 = getStuff(1, 200);
const s2 = getStuff(2, 100);
const s3 = getStuff(3, 250);
// ...
Luego cree una cadena de reacción sobre cómo procesarlos (stuff1 antes de stuff2, stuff2 antes de stuff3, etc.)
// create a chain of reaction order to the results of parallel promises
s1
.then(console.log) // s1 resolved: log result
.then(() => s2) // chain s2
.then(console.log) // s2 resolved: log result
.then(() => s3) // chain s3
// ...
.then(() => { // chain another function at at the end for when all promises resolved
// all promises resolved (all data was logged)
}
Para reaccionar a los resultados de promesa en el mismo orden en que se crearon las promesas, puede cambiar su función getStuff
para encadenar dinámicamente las reacciones usando Array.prototype.reduce
:
var times = [200, 100, 250, 200, 300, 250, 5000];
var getStuff = function(time, index) { // swap the order of arguments so number is the index passed in from Array.map
return new Promise((resolve, reject) => {
window.setTimeout(() => {
resolve(`${index + 1} - Done.`); // use index + 1 because indexes start at 0
}, time);
});
};
times
// map each time to a promise (and number to the index of that time + 1) and fire of a request
.map(getStuff)
// dynamically build a reaction chain for the results of promises
.reduce((chain, promise) => {
return chain
.then(() => promise)
.then(console.log);
}, Promise.resolve())
.then(() => {
// all promises resolved (all data was logged in order)
});
Sé que no es nativo, pero con bluebird puede usar Promise.some
(para cumplir después de que se hayan cumplido las promesas) o Promise.mapSeries
(para cumplir las promesas en serie) de alguna manera para lograr el flujo que espera.
Promise.all()
normal: puede usar Promise.all()
forma segura. Los ejecutores de promesas se dispararán en paralelo y los resultados se devolverán en el orden en que inserta sus promesas en la matriz de promesas. Entonces depende de ti ordenarlos como quieras. Como en el siguiente fragmento de código, tenemos cinco promesas que cada una se resolverá aleatoriamente en 5 segundos. Independientemente de su tiempo de resolución, obtendrá los resultados cuando se resuelva la última;
var promises = [ new Promise( v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))),
];
Promise.all(promises)
.then(result => console.log(result.reduce((p,c) => p + "/n" + c)));
Lo que quiere: Pero luego no quiere esperar mientras termina la última, sino que desea procesarlos en el orden, tan pronto como se resuelvan. Entonces Array.prototype.reduce()
es tu mejor amigo aquí. Como
var promises = [ new Promise( v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))),
new Promise( v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000)))
];
promises.reduce((p,c) => p.then(result => (console.log(result + "/n"),c)))
.then(result => (console.log(result + "/n")));
Ejecute el código varias veces para ver cómo se comporta el código. El texto se actualizará tan pronto como la promesa se resuelva, pero solo si es su turno. Entonces, si la 1ª resolución se resuelve después de la 2ª, veremos que la 1ª y la 2ª aparecen de inmediato en su orden, pero no esperarán a que la 3ª se resuelva, etc.