私が理解している限りでは、ES7/ES2016では、複数のawait
をコードに入れることは、.then()
をpromiseでチェーニングするのと同じように機能します。それで、例えば、我々はこのコードを持っています:
await someCall();
await anotherCall();
anotherCall()
は、someCall()
が完了したときにのみ呼び出されると正しく理解していますか?それらを並行して呼び出す最もエレガントな方法は何ですか?
Nodeで使用したいのですが、非同期ライブラリを使用したソリューションはあるのでしょうか。
編集:私はこの質問で提供される解決策に満足していない: 非同期ジェネレータでの約束を並行して待っていないためにスローダウン それはジェネレータを使用し、私はより一般的なユースケースについて尋ねている。
あなたはPromise.all()で待つことができます:
await Promise.all([someCall(), anotherCall()]);
TL; DR
並列関数呼び出しにはPromise.all
を使用してください。エラーが発生したときの応答動作が正しくありません。
まず、非同期呼び出しを一度にall実行し、すべてのPromise
オブジェクトを取得します。次に、await
オブジェクトでPromise
を使用します。この方法では、最初のPromise
が他の非同期呼び出しを解決するのを待っている間、まだ進行中です。全体として、最も遅い非同期呼び出しの間だけ待つことになります。例えば:
// Begin first call and store promise without waiting
const someResult = someCall();
// Begin second call and store promise without waiting
const anotherResult = anotherCall();
// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];
// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise
JSbinの例: http://jsbin.com/xerifanima/edit?js,console
警告:await
呼び出しは、最初のawait
呼び出しはすべての非同期呼び出しの後にafter発生します。 JohnnyHKのコメントを参照してください。
更新:@ bergi's answer に応じて、この回答はエラー処理のタイミングが異なります。NOTエラーが発生するとエラーをスローしますが、すべてのプロミスが実行された後です。結果を@jonnyのヒントと比較します:[result1, result2] = Promise.all([async1(), async2()])
、次のコードスニペットを確認します
const correctAsync500ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, 'correct500msResult');
});
};
const correctAsync100ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 100, 'correct100msResult');
});
};
const rejectAsync100ms = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, 'reject100msError');
});
};
const asyncInArray = async (fun1, fun2) => {
const label = 'test async functions in array';
try {
console.time(label);
const p1 = fun1();
const p2 = fun2();
const result = [await p1, await p2];
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
const asyncInPromiseAll = async (fun1, fun2) => {
const label = 'test async functions with Promise.all';
try {
console.time(label);
let [value1, value2] = await Promise.all([fun1(), fun2()]);
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
(async () => {
console.group('async functions without error');
console.log('async functions without error: start')
await asyncInArray(correctAsync500ms, correctAsync100ms);
await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
console.groupEnd();
console.group('async functions with error');
console.log('async functions with error: start')
await asyncInArray(correctAsync500ms, rejectAsync100ms);
await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
console.groupEnd();
})();
更新:
元の答えでは、約束の拒否を正しく処理することが困難(場合によっては不可能)になります。正しい解決策はPromise.all
を使うことです。
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
元の答え:
どちらか一方を待つ前に、必ず両方の関数を呼び出すようにしてください。
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
私は a Gist を作成して、約束を解決するいくつかの異なる方法をテストし、結果を出しています。うまくいくオプションを見ることは役に立つかもしれません。
Promise.all()を使わずに並行して行う別の方法があります。
まず、数字を印刷するための2つの関数があります。
function printNumber1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number1 is done");
resolve(10);
},1000);
});
}
function printNumber2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number2 is done");
resolve(20);
},500);
});
}
これは順次です。
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
これは平行です。
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
// A generic test function that can be configured
// with an arbitrary delay and to either resolve or reject
const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
console.log(`Done ${ delay }`);
resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
}, delay));
// Our async handler function
const handler = async () => {
// Promise 1 runs first, but resolves last
const p1 = test(10000, true);
// Promise 2 run second, and also resolves
const p2 = test(5000, true);
// Promise 3 runs last, but completes first (with a rejection)
// Note the catch to trap the error immediately
const p3 = test(1000, false).catch(e => console.log(e));
// Await all in parallel
const r = await Promise.all([p1, p2, p3]);
// Display the results
console.log(r);
};
// Run the handler
handler();
/*
Done 1000
Reject 1000
Done 5000
Done 10000
*/
P1、p2、およびp3の設定は厳密に並行して実行されませんが、実行を保留せず、キャッチでコンテキストエラーをトラップできます。