Параллельные операции с Promise.all?

Я убежден, что Promise.all выполняет все функции, которые вы передаете ему параллельно, и не волнует, какой заказ вернул возвращаемый promises.

Но когда я пишу этот тестовый код:

function Promise1(){
    return new Promise(function(resolve, reject){
        for(let i = 0; i < 10; i++){
            console.log("Done Err!");
        }
        resolve(true)
    })
}

function Promise2(){
    return new Promise(function(resolve, reject){
        for(let i = 0; i < 10; i++){
            console.log("Done True!");
        }
        resolve(true)
    })
}

Promise.all([ 
    Promise1(),
    Promise2()
])
.then(function(){
    console.log("All Done!")
})

В результате получается

Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done!

Но если они работают параллельно, я не ожидаю, что они будут выполняться одновременно и дать мне такой результат?

Done Err!
Done True!
Done Err!
Done True!
Done Err!
Done True!
Done Err!
Done True!
Etc. Etc.?

Или я что-то пропущу, как я это делаю?

Ответ 1

Это потому, что ваш Promises блокирует и синхронно! Попробуйте что-то с таймаутом вместо синхронного цикла:

function randomResolve(name) {
  return new Promise(resolve => setTimeout(() => {
    console.log(name);
    resolve();
  }, 100 * Math.random()));
}

Promise.all([ 
    randomResolve(1),
    randomResolve(2),
    randomResolve(3),
    randomResolve(4),
])
.then(function(){
    console.log("All Done!")
})

Ответ 2

Неасинхронное тело выполняется последовательно Когда вы достигнете вызова Async внутри вашего тела (например, удалите URL-адрес), другой Promises в массиве начнет выполнение.

Ответ 3

Чтобы развить то, что начал Йоханнес Мерц, я предлагаю этот код, чтобы прояснить, что все происходит параллельно.

JS является однопоточным, но Node.js имеет множество инструментов для явного и неявного запуска дополнительных потоков. Обещания предоставляют больше функциональности, которая нам часто нужна, без необходимости явного запуска новых потоков или процессов. Promise.all() является таким примером, но вы должны быть довольны тем, что Promises использует его, не создавая для себя серьезных головных болей, таких как утечки памяти в Promise Scope.

function randomResolve(name,t) {
  return new Promise(resolve => setTimeout(() => {
    console.log({ name, t });
    resolve({ name, t });
  }, t));
}

(() => {
    // Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
    let start = new Date().valueOf(); 

    Promise.all([ 
        randomResolve(1, 1000 * Math.random()),
        randomResolve(2, 1000 * Math.random()),
        randomResolve(3, 1000 * Math.random()),
        randomResolve(4, 1000 * Math.random()),
    ])
    .then(function( res ){
        console.info( res );
        console.log("All Done!", parseInt(new Date().valueOf() - start) );
    })
})();

Этот шаблон принимает массив входных данных и использует array.map() для возврата массива запущенных обещаний, которые будут обрабатываться параллельно, как описано выше. Обратите внимание, что здесь нельзя использовать async/await.

function randomResolve(name,t) {
  return new Promise(resolve => setTimeout(() => {
    console.log({ name, t });
    resolve({ name, t });
  }, t));
}

(() => {
    // Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
    let start = new Date().valueOf(),
        vals = [ 
            [1, 1000 * Math.random()],
            [2, 1000 * Math.random()], 
            [3, 1000 * Math.random()],
            [4, 1000 * Math.random()]
        ];

    Promise.all( vals.map( v => { return randomResolve(v[0], v[1] ); } ) )
    .then(function( res ){
        console.info( res );
        console.log("All Done!", parseInt(new Date().valueOf() - start) );
    })
})();

В этой версии реализована асинхронная/ожидание.

function randomResolve(name,t) {
  return new Promise(resolve => setTimeout(() => {
    console.log({ name, t });
    resolve({ name, t });
  }, t));
}

(async () => {
    // Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
    let start = new Date().valueOf(),
        vals = [ 
            [1, 1000 * Math.random()],
            [2, 1000 * Math.random()], 
            [3, 1000 * Math.random()],
            [4, 1000 * Math.random()]
        ];

    let res = await Promise.all( vals.map( async v => { return await randomResolve( v[0], v[1] ); } ) );
    // await the Promise.aall() call instead of using .then() afterwards with another closure then
    //     forEach v in vals, start and await a Promise from randomResolve() then return the result to map

    console.info( res );
    console.log("All Done!", parseInt(new Date().valueOf() - start) );

})();

Ответ 4

Я бы предложил использовать это так:

const [
    res1,
    res2
] = await Promise.all([
    asyncCall1(),
    asyncCall1(),
]);