Вы можете использовать Promise.all
, чтобы по-прежнему выполнять большую часть своей работы одновременно и получать результаты, когда они все будут выполнены, в том же порядке, в котором вы их запустили. Вот полностью рабочий пример, который несколько напоминает вашу проблему.
// Generate a random value between 250 and 500. Used for the delay to setTimeout
// to mimic "long running" operations.
const delay = () => Math.floor(Math.random()*(500 - 250 + 1))+250;
// This represents your model.aggregate function. Takes an input of a number for
// the sake of correlation in this example. Always resolves a string with the
// intention of passing it into the next "then."
const aggregate = (i) => {
return new Promise((resolve, reject) => {
setTimeout(() => { resolve(`[${i}]: aggregated`) }, delay());
});
};
// This represents your model.update function. Takes a number for the sake of
// correlation and the result from the "aggregate" function.
const update = (i, res) => {
// result from aggregate
console.log(`[${i}]: ${res}`);
return new Promise((resolve, reject) => {
setTimeout(() => { resolve(`[${i}]: updated`) }, delay());
});
};
// A helper to generate an array of numbers. Just a "fancier" alternative to
// "for i=value; i<count; i++"
const range = (i) => [...Array(i).keys()]
// An arbitrary number for the amount of work to run in this example. This
// represents "count" in your example.
const count = 10;
// This is the equivalent of what would be your for loop's body. Executes "count"
// amount of work and stores all the the promises into an array. Even though we
// have a list of promises in the array, this is non-blocking and the program will
// continue until we wait for them to all resolve later.
const res = range(count).map((i) => {
console.log(`[${i}]: starting`);
return aggregate(i).then((res) => update(i, res));
});
// Wait for all promises to resolve and print the final results. At this
// exact moment, some be still running and some may have completed.
// Call then will let you get the results of all the work when they are
// are all resolved.
//
// The block in "catch" will be called when any one of them "rejects" or
// throws an exception.
Promise.all(res).
then((vals) => { for (const val of vals) { console.log(val) } }).
catch((err) => { console.error(err) });
Вот пример выходных данных:
[0]: starting
[1]: starting
[2]: starting
[3]: starting
[4]: starting
[5]: starting
[6]: starting
[7]: starting
[8]: starting
[9]: starting
[6]: [6]: aggregated
[2]: [2]: aggregated
[4]: [4]: aggregated
[7]: [7]: aggregated
[9]: [9]: aggregated
[3]: [3]: aggregated
[0]: [0]: aggregated
[1]: [1]: aggregated
[8]: [8]: aggregated
[5]: [5]: aggregated
[0]: updated
[1]: updated
[2]: updated
[3]: updated
[4]: updated
[5]: updated
[6]: updated
[7]: updated
[8]: updated
[9]: updated
Обратите внимание, что конечные выходные данные находятся в том же порядке, что и их запуск ( От 0 до 9). Однако «агрегат» и «обновление» по-прежнему выполняются асинхронно.
Дело в том, что вам не нужно ждать запуска первого вызова «агрегат затем обновление» для fini sh следующий «агрегат, затем обновление», затем следующий «агрегат, затем обновление» после этого и так далее. Довольно сильно выбрасывая "асинхронность" в окно. Использование async / await определенно имеет свое место (и я их часто использую), но я бы сказал, что это не лучшее место для его использования.