async/await에서 에러 처리
new Promise((resolve, reject) => {
throw new Error('this is error')
}).catch(e => console.log(e)) // 에러내용 출력
new Promise(async (resolve, reject) => {
throw new Error('this is error')
}).catch(e => console.log(e)) // pending
Promise 안에서 async를 사용하여 Error를 반환한 두번째의 경우에는 에러가 정상적으로 반환되지 않습니다.
이 상태면 Promise는 계속 pending 상태가 되고, 메모리를 차지하고 있게 됩니다.
왜 그럴까요?
promise 내부에서는 resolve, reject를 통해 결과값을 컨트롤 해야만 합니다.
두개를 이용하지 않은 경우에는 아직 pending 상태로 인식하게 되고 fullfilled, reject 상태로 넘어갈 수가 없습니다.
그래서 아래와 같은 두 개의 케이스중 하나로 우회하여야 합니다.
new Promise(async (resolve, reject) => {
reject('this is Error');
}).catch(e => console.log(e))
new Promise(async (resolve, reject) => {
try {
throw new Error('this is error')
} catch(e) {
reject(e)
}
}).catch(e => console.log(e))
async 내부에서 promise의 reject를 이용하거나 try/catch를 이용하여 에러를 캐치하는 경우입니다.
forEach/map 에서 async/await 사용
이 케이스는 실제로 겪고나서 promise.all로 해결한 사례입니다.
사실 병렬처리를 이용하는 Promise.all을 이용하는 것이 맞았던 상황이였긴 했지만
그 당시에는 promise의 쓰임새에 부족한 지식을 가지고 있었고 왜 안되지 하면서 헤맸었던 기억이 있습니다.
const myPromise = (input) => Promise.resolve(input)
(async () => {
const names = ['logan', 'chulsoo', 'minsoo']
const results = []
names.forEach(async (name) => {
const result = await myPromise(name)
results.push(result)
})
console.log(results) // []
})()
예측한 결과는 results === names 였는데 의도와는 다르네요
위의 결과가 일어나는 이유는 root async에서 forEach에는 await이 없으므로 기다려주지 않고 바로 console.log 실행으로 옮겨가기 때문입니다.
아래에는 3가지 방법이 있습니다.
// 1번 //
(async () => {
const names = ['logan', 'chulsoo', 'minsoo']
const results = []
await names.forEach(async (name) => {
const result = await myPromise(name)
results.push(result)
})
console.log(results) // ['logan', 'chulsoo', 'minsoo']
})()
// 2번 //
(async () => {
const names = ['logan', 'chulsoo', 'minsoo']
const promises = names.map(name => myPromise(name))
const results = await Promise.all(promises)
console.log(results) // ['logan', 'chulsoo', 'minsoo']
})()
// 3번 //
(async () => {
const names = ['logan', 'chulsoo', 'minsoo']
const results = []
for ( let name of names) {
results.push(await myPromise(name))
}
console.log(results) // ['logan', 'chulsoo', 'minsoo']
})()
1번 => 가장 간단한 방법입니다. await을 사용하여 root async에서 기다려줄 수 있도록 기다려주면 됩니다.
2번 => Promise.all을 사용하는 방법입니다. Promise.all을 사용하면 Promise.all에 들어온 promise 배열을 병렬로 실행하게 됩니다. 동시에 요청, 오래걸리는 요청이 있는경우 promise.all을 활용하여야 합니다.
3번 => 1번과 2번의 단점은 순서를 보장받을 수 없습니다.
3번은 순서를 보장받을 수 있는 방법입니다.
root async에서는 for문 내의 await을 인식할 수 있습니다. async/await은 함수 스코프로 동작하기 때문입니다.
함수 스코프를 없애주면 root async에서는 await을 인식하게 됩니다. 그래서 기다려 주는 것이지요
Promise 병렬로 사용하기
여러 요청이 Serialize 하게 chainig이 걸려있다면 사용자의 대기시간은 자연스레 증가하게 됩니다.
병렬로 요청을 할 수 있는 경우에는 이 방법을 사용하여 사용자의 대기시간을 줄여야 합니다.
사용자의 대기시간이 길어진다는 것은 곧 탈출을 의미하게 되고 매출을 올릴수가 없습니다. (결국 개발도 서비스직 아닌가 생각합니다.)
// Simple gist to test parallel promise resolution when using async / await
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
async function test() {
return [
await promiseWait(1000),
await promiseWait(5000),
await promiseWait(9000),
await promiseWait(3000),
]
}
async function test2() {
return {
'aa': await promiseWait(1000),
'bb': await promiseWait(5000),
'cc': await promiseWait(9000),
'dd': await promiseWait(3000),
}
}
async function test3() {
return await {
'aa': promiseWait(1000),
'bb': promiseWait(5000),
'cc': promiseWait(9000),
'dd': promiseWait(3000),
}
}
async function test4() {
const p1 = promiseWait(1000);
const p2 = promiseWait(5000);
const p3 = promiseWait(9000);
const p4 = promiseWait(3000);
return {
'aa': await p1,
'bb': await p2,
'cc': await p3,
'dd': await p4,
};
}
async function test5() {
return await Promise.all([
await promiseWait(1000),
await promiseWait(5000),
await promiseWait(9000),
await promiseWait(3000),
]);
}
async function test6() {
return await Promise.all([
promiseWait(1000),
promiseWait(5000),
promiseWait(9000),
promiseWait(3000),
]);
}
async function test7() {
const p1 = promiseWait(1000);
const p2 = promiseWait(5000);
const p3 = promiseWait(9000);
return {
'aa': await p1,
'bb': await p2,
'cc': await p3,
'dd': await promiseWait(3000),
};
}
let start = Date.now();
test().then((res) => {
console.log('Test Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test2().then((res) => {
console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test3().then((res) => {
console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test4().then((res) => {
console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test5().then((res) => {
console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test6().then((res) => {
console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res);
});
start = Date.now();
test7().then((res) => {
console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res);
});
});
});
});
});
});
/*
Test Done, elapsed 18.006 [ true, true, true, true ]
Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true }
Test3 Done, elapsed 0 { aa: Promise { <pending> },
bb: Promise { <pending> },
cc: Promise { <pending> },
dd: Promise { <pending> } }
Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true }
Test5 Done, elapsed 18.008 [ true, true, true, true ]
Test6 Done, elapsed 9.003 [ true, true, true, true ]
Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true }
*/
tes4번과 test6번은 병렬적으로 수행이 됩니다.
가장 가독성 있고 직관적인 것은 Promise.all의 사용이 아닐까 싶습니다.
Promise.all은 모든 Promise가 끝나야 하고 결과가 모두 positivit여야 합니다.
Promise.race, Prosmise.allSettled와 같은 도구들도 있어서 활용하면 좋습니다.
test4번의 경우 Promise객체를 변수에 담으면 promise가 바로 실행되는 것으로 알고 있습니다.
이후에 await을 이용하는데 이 경우는 좀 더 공부해서 내용 추가하겠습니다.
출처 : https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&blogId=bitofsky&logNo=220970024616
https://stackoverflow.com/questions/35612428/call-async-await-functions-in-parallel
'Frontend > Javascript' 카테고리의 다른 글
Javascript undefined/undeclared/null and NaN 🧟 (0) | 2022.04.23 |
---|---|
Javascript intersection observer api 👀 (0) | 2022.04.20 |
Javascript 콜백 📞 (0) | 2022.04.19 |
Javascript Promise, async/await MicroTask queue 동작차이 🖇 (1) | 2022.04.15 |
Javascript Prototype 🤢 (0) | 2022.04.15 |