자바스크립트를 처음 공부하다 보면 이런 난관에 빠지게 된다.
아래 코드는 비동기함수를 이용해서 arr 배열에 값을 넣는 코드이다.
비동기 함수를 사용하고 있기에 async, await를 사용해서 제대로 arr 배열에 값이 들어가도록 하고 있다.
const a = new Array(10).fill(1)
const arr = []
const asyncFunction = (i) => {
return new Promise(resolve => {
setTimeout(() => {
arr.push(i)
resolve()
}, 100);
})
}
(async() => {
for (let i = 0; i < a.length; i++) {
await asyncFunction(i)
}
console.log(arr)
})()
이 코드를 실행하면 예상대로 arr 값에 정상적으로 값이 들어가는 것을 알 수 있다.
그러면 아래 코드는 어떨까?
for문은 아름답지 않기에 for 대신 forEach를 사용해서 값을 넣어보도록 하자
const a = new Array(10).fill(1);
const arr = []
const asyncFunction = (i) => {
return new Promise(resolve => {
setTimeout(() => {
arr.push(i)
resolve()
}, 1000);
})
}
a.forEach(async (_, i) => {
await asyncFunction(i)
})
console.log(arr)
결과 값은 어떨까?
당연히 바뀐건 forEach로 바뀐 것밖에 없으니 정상적으로 나올 것처럼 보인다.
그러나 결과값을 보면 빈 배열 값이 나오는 것을 볼 수 있는데 이유가 뭘까?
이렇게 되는 이유는 forEach가 await를 기다려주지 않기 때문이다.
요즘 리액트나 뷰 같은 SPA에서 자주 쓰는 axios도 비동기가 기본인데 이렇게 되면 forEach에서 axios를 쉽게 쓸 수 없는 것.
이 현상은 forEach뿐 아니라 reduce, map, filter 등의 Array prototype 메서드에서 나타난다.
그러면 어떻게 해야할까?
비동기 함수를 쓰려면 옛날 방식인 for문을 사용해야 할까?
일단 for문 대신 사용할 수 있는 방법은 for ... of 가 있다.
const a = new Array(10).fill(1)
const arr = []
const asyncFunction = (i) => {
return new Promise(resolve => {
setTimeout(() => {
arr.push(i)
resolve()
}, 100);
})
}
(async() => {
for (let i = 0; i < a.length; i++) {
await asyncFunction(i)
}
console.log(arr)
})()
다른 방법으로는 map과 Promise.all을 사용하는 방법이 있다.
const a = new Array(10).fill(1);
const arr = []
const asyncFunction = (i) => {
return new Promise(resolve => {
setTimeout(() => {
arr.push(i)
resolve()
}, 1000);
})
}
const promise = a.map(async (_, i) => {
await asyncFunction(i)
})
Promise.all(promise)
.then(() => console.log(arr))
forEach 사용하면서 당황하는 일이 없도록 하자
'javascript' 카테고리의 다른 글
가상 DOM(Virtual DOM)은 무엇인가? 가상 DOM은 실제 DOM보다 빠른가? (0) | 2024.05.27 |
---|---|
깊은 복사와 얕은 복사와 Spread Operator(...object)에 대해 알아보자 (0) | 2024.03.07 |
input 태그 image를 base64 형식으로 변경하기 (0) | 2021.09.23 |