반응형
코드를 짜다보면 비동기함수를 동기처리하여 순차적으로 실행시켜야할 일이 생기기 마련이다.
내가 구현하려했던 동작은 이거였다.
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//하이
반복문으로 짤수도 있지만, 고차함수로 짜는것이 좀더 가독성이 좋다고 생각해서 처음에는 forEach문을 활용해서 비동기함수를 동기처리해주고자 했다.
forEach내부에서 await의 문제
초기 코드를 비동기처리 함수를 기다렸다가 동기적으로 진행하기위해 이런식으로 짰다.
const timeout = () =>
new Promise((resolve) => {
setTimeout(() => resolve('timeout 완료'), 1000);
});
Array.from({ length: 3 }).forEach(async () => {
console.log(await timeout());
});
console.log('하이');
forEach (async()=> ...) 반복문이 동기적으로 실행되기를 기대했으나 결과는 다음과 같다.
예상결과
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//하이
실행결과
//하이
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
놀랍게도 여전히 비동기적으로 실행되고 있다.
forEach는 함수이기 때문에 한번 실행되고, 바로 console.log를 찍어버린다.
그리고 forEach내부에서는 동기적으로 1초씩 기다렸다가 수행되는 것이다.
map과 Promise.all로 해결해보기
비동기함수의 이점을 그대로 챙기면서, 외부함수에 대해서 순차적으로 실행되게 하기위해 이렇게 작성해줄 수 있다.
const timeout = () =>
new Promise((resolve) => {
setTimeout(() => resolve('timeout 완료'), 1000);
});
const promises = Array.from({ length: 3 }).map(timeout);
const main = async () => {
const result = await Promise.all(promises);
result.forEach((res) => console.log(res));
console.log('하이');
};
main();
이러면 1초만에 3개의 함수가 모두 실행되고, '하이'는 그 뒤에 찍히게 된다.
// (1초뒤...)
//timeout 완료
//timeout 완료
//timeout 완료
//하이
예상대로 작동하게 하기
forEach는 외부 문맥에 대해 동기적으로 작동하지 않으므로, for...of문을 사용해서 예상한대로 작동하게 할 수 있다.
const timeout = () =>
new Promise((resolve) => {
setTimeout(() => resolve('timeout 완료'), 1000);
});
const main =async ()=>{
for(let x=0; x<3; x++){
console.log(await timeout());
}
console.log('하이');
}
이렇게 하면 생각했던 대로 작동한다.
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//timeout 완료 (1초뒤...)
//하이
반응형