첫 번째 차이점-빨리 실패
@zzzzBov의 답변에 동의하지만 Promise.all의 "빠른 실패"이점은 한 가지 차이점이 아닙니다. 일부 사용자는 부정적인 시나리오에서 Promise.all이 더 빠를 때 (일부 작업이 실패한 경우) 왜 사용하는지 묻습니다. 그리고 왜 안 물어? 두 개의 독립적 인 비동기 병렬 작업이 있고 첫 번째 작업이 매우 오랜 시간에 해결되었지만 두 번째 작업이 매우 짧은 시간에 거부되면 사용자가 "매우 짧은 시간"대신 "매우 긴 시간"오류 메시지를 기다리는 이유는 무엇입니까? 실제 응용 프로그램에서는 부정적인 시나리오를 고려해야합니다. 그러나 첫 번째 차이점에서 Promise.all과 다중 대기를 사용할 대체 방법을 결정할 수 있습니다.
두 번째 차이점-오류 처리
그러나 오류 처리를 고려할 때는 반드시 Promise.all을 사용해야합니다. 다중 대기로 트리거 된 비동기 병렬 태스크의 오류를 올바르게 처리 할 수 없습니다. 부정적인 시나리오에서 당신은 항상로 끝납니다 UnhandledPromiseRejectionWarning
그리고 PromiseRejectionHandledWarning
당신이 시도 / 캐치 어디를 사용하지만. 이것이 Promise.all이 설계된 이유입니다. 물론 누군가는 우리가 사용하여 해당 오류를 억제 할 수 있다고 말할 수 process.on('unhandledRejection', err => {})
및 process.on('rejectionHandled', err => {})
하지만 좋은 방법이 아닙니다. 인터넷에서 두 개 이상의 독립적 인 비동기 병렬 작업에 대한 오류 처리를 고려하지 않거나 전혀 고려하지 않지만 잘못된 방법으로 시도하는 많은 예제를 찾았습니다 .try / catch를 사용하고 오류를 잡기를 바랍니다. 좋은 습관을 찾는 것은 거의 불가능합니다. 이것이 제가이 답변을 쓰는 이유입니다.
요약
오류를 심각하게 처리 할 수 없기 때문에 둘 이상의 독립적 인 비동기 병렬 작업에는 다중 대기를 사용하지 마십시오. 이 사용 사례에는 항상 Promise.all ()을 사용하십시오.
Async / await는 약속을 대체하지 않습니다. 그것은 약속을 사용하는 방법과 매우 똑같습니다 ... 비동기 코드는 동기화 스타일 로 작성 되며 우리는 여러 then
약속을 피할 수 있습니다 .
어떤 사람들은 Promise.all ()을 사용하여 태스크 오류를 개별적으로 처리 할 수 없지만 첫 번째 거부 약속의 오류 만 처리 할 수 있다고 말합니다 (예 : 일부 유스 케이스는 로깅 등의 별도 처리가 필요할 수 있음). 문제가되지 않습니다. 아래의 "추가"제목을 참조하십시오.
예
이 비동기 작업을 고려하십시오 ...
const task = function(taskNum, seconds, negativeScenario) {
return new Promise((resolve, reject) => {
setTimeout(_ => {
if (negativeScenario)
reject(new Error('Task ' + taskNum + ' failed!'));
else
resolve('Task ' + taskNum + ' succeed!');
}, seconds * 1000)
});
};
긍정적 인 시나리오에서 작업을 실행할 때 Promise.all과 여러 대기간에 차이가 없습니다. 두 예제 모두 Task 1 succeed! Task 2 succeed!
5 초 후에 끝납니다 .
// Promise.all alternative
const run = async function() {
// tasks run immediate in parallel and wait for both results
let [r1, r2] = await Promise.all([
task(1, 5, false),
task(2, 5, false)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
// multiple await alternative
const run = async function() {
// tasks run immediate in parallel
let t1 = task(1, 5, false);
let t2 = task(2, 5, false);
// wait for both results
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
긍정적 인 시나리오에서는 첫 번째 작업에 10 초가 걸리고 부정적인 시나리오에서는 몇 초의 작업에 5 초가 걸리면 발행 된 오류에 차이가 있습니다.
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
우리는 이미 여러 대기를 동시에 사용할 때 잘못된 일을하고 있음을 주목해야합니다. 물론 오류를 피하려면 처리해야합니다! 해보자...
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: Caught error Error: Task 2 failed!
오류를 성공적으로 처리하는 것을 볼 수 있듯이 run
함수에 하나의 catch 만 추가 하면 catch 논리가있는 코드가 콜백 ( async style )에 있습니다. run
비동기 함수는 자동으로 수행되므로 함수 내부에서 핸들 오류가 필요하지 않습니다 . 함수 task
거부는 run
함수 거부를 유발 합니다. 콜백을 피하기 위해 동기화 스타일 (async / await + try / catch)을 사용할 수 try { await run(); } catch(err) { }
있지만이 예제 await
에서는 메인 스레드에서 사용할 수 없기 때문에 불가능 합니다-비동기 함수에서만 사용할 수 있습니다 (아무도 원하지 않기 때문에 논리적입니다) 메인 스레드 차단). 다른 비동기 함수의 동기화 스타일 우리가 호출 할 수 있습니다run
함수에서 처리가 작동하는지 테스트 하거나 IIFE (즉시 호출 된 함수 표현식)를 사용하려면 다음을 수행하십시오 (async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.
이것은 둘 이상의 비동기 병렬 작업을 실행하고 오류를 처리하는 올바른 방법입니다. 아래 예제는 피해야합니다.
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
우리는 여러 가지 방법으로 코드를 처리하려고 시도 할 수 있습니다 ...
try { run(); } catch(err) { console.log('Caught error', err); };
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled
... 동기 코드를 처리하지만 run
비동기
이기 때문에 아무것도 발견되지 않았습니다.
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... Wtf? 먼저 작업 2의 오류가 처리되지 않았으며 나중에 발견 된 것을 알 수 있습니다. 콘솔에서 잘못되어 여전히 오류가 가득합니다. 이 방법으로는 사용할 수 없습니다.
(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... 위와 동일합니다. 삭제 된 답변의 @ Qwerty 사용자는 잡히는 것처럼 보이지만 처리되지 않은 오류가있는이 이상한 행동에 대해 물었습니다. run ()이 await 키워드와 함께 거부되어 run ()을 호출 할 때 try / catch를 사용하여 잡을 수 있기 때문에 오류가 발생합니다. 비동기 태스크 함수를 동기식으로 호출하고 (대기 키워드없이)이 태스크는 run () 함수 외부에서 실행되며 외부에서도 실패하기 때문에 처리되지 않은 오류가 발생합니다. setTimeout ...에서 코드의 일부를 실행하는 동기화 함수를 호출 할 때 try / catch로 오류를 처리 할 수없는 경우와 비슷 function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
합니다.
const run = async function() {
try {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
}
catch (err) {
return new Error(err);
}
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... "오직"두 오류 (3 번째 오류가 누락 됨)이지만 아무것도 발견되지 않았습니다.
추가 (작업 오류를 별도로 처리하고 첫 번째 오류도 처리)
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, true).catch(err => { console.log('Task 1 failed!'); throw err; }),
task(2, 5, true).catch(err => { console.log('Task 2 failed!'); throw err; })
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Run failed (does not matter which task)!'); });
// at 5th sec: Task 2 failed!
// at 5th sec: Run failed (does not matter which task)!
// at 10th sec: Task 1 failed!
...이 예제에서는 두 작업 모두에 negativeScenario = true를 throw err
사용하여 발생하는 상황을보다 잘 보여줍니다 ( 최종 오류를 발생시키는 데 사용됨).