web-dev-qa-db-ja.com

Promise.allを使用した並列操作?

Promise.allは、渡されたすべての関数を並行して実行し、返されたpromiseが終了する順序を気にしません。

しかし、このテストコードを書くとき:

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.?

または、私はそれをやっている方法で何かを見逃していますか?

12
Steve

Promiseがブロックされ、同期しているためです!同期ループの代わりにタイムアウトで何かを試してください:

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!")
})
11
Johannes Merz

次のように使用することをお勧めします。

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

Johannes Merzが始めたことに基づいて、このコードを提案して、物事が並行して起こっていることを明確にします。

JSはシングルスレッドですが、Node.jsには追加のスレッドを明示的および暗黙的に起動するためのツールがたくさんあります。 Promiseは、明示的に新しいスレッドまたはプロセスを起動することなく、多くの場合必要な機能を公開します。 Promise.all()はそのような例ですが、Promise Scopeのメモリリークなどの深刻な頭痛の種を作成せずにPromisesを使用するには、Promisesに慣れる必要があります。

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

このバージョンはasync/awaitを実装します。

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

})();
0
rainabba

非非同期ボディはシリアルで実行されますボディ内で非同期呼び出しに到達すると(例:URLにヒット)、配列内の他のプロミスが実行を開始します。

0
Sanket Berde