forEach의 콜백함수에서는 async함수를 사용할 수 없다. 반면 for 또는 for...of에서는 사용가능하다. 그 이유가 무엇일까??
이유를 알아보기전 for 또는 for...of는 어떻게 async를 적용시키는지 간단하게만 보고 넘어가자.
for...of
문은 아래와 같이 간단하게 async, await를 적용시킬 수 있다.
const loopAsync = async(fn) =>{
for(const x of arr){
const res = await fn(x)
console.log(res)
}
}
반면에 forEach는 안된다. 그 이유를 알아보자.
forEach는 아래와 같은 코드로 동작하기 때문에 await가 먹히지 않는다.
Array.prototype.forEach = function (callback) {
for (let index = 0; index < this.length; index++) {
callback(this[index], index, this);
}
};
아래와 같이 코드를 실행하면 1초 뒤에 1,2,3,4,5가 표푝 나오게된다. why? 위의 코드를 보고 파악해보자.
async,await가 적용된 콜백함수를 위의 forEach함수에 넣는것이다. forEach는 async 함수가 아니기 때문에 for문에서 콜백함수가 실행될 때 완료를 기다리지 않고 일단 콜백함수를 실행만시키며 넘어간다. (이 설명은 많은 내용이 생략된 개략적인 내용이라고 한다. 나는 forEach는 왜 콜백함수에 async await 함수를 사용할 수 없는지만 이해하고 넘어갔다.)
const arr = [1, 2, 3, 4, 5];
const getDelay = (v, time) => new Promise((resolve) => setTimeout(() => resolve(v), time));
//value를 받아서 1초뒤에 출력하는 콜백함수
const cbFn = async (value) => {
const res = await getDelay(value, 1000);
log(res);
};
arr.forEach(cbFn);
//1초뒤에 1,2,3,4,5
다음 예시를 통해 loop함수(위에있는 forEach와 비슷한)가 async 함수인지 아닌지로 어떤 결과 차이가 나는지 확인할 수 있다.
const loop = (fn) => {
for (let i = 0; i < 5; i++) {
fn(i);
}
};
const loopAsync = async (fn) => {
for (let i = 0; i < 5; i++) {
await fn(i);
}
};
loop(cbFn); //1초뒤에 12,3,4,5 빠박
loopAsync(cbFn); // 차근차근 1초뒤에 출력
비동기적으로 작동하는 forEach forEachAsync
를 만들어서 그걸 사용하면 forEach도 콜백함수에 async,await함수를 사용할 수 있다!
const forEachAsync= async (arr,cbFn) => {
for(let i =0; i<arr.length; i++){
await cbFn(arr[i], i, arr)
}
}
비동기적으로 실행시켜주는 reduce도 만들어보자. 인자는 순서대로 (콜백함수, 초기값, 이터러블객체)가 들어온다.
const reduceAsync = async (cbFn, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const x of iter) {
acc = await cbFn(acc, x);
}
return acc;
};
잘 만들어 졌는지 아래 코드로 실험해보자.
cbFn
(콜백함수) : acc,value를 인자로 받고 acc+value를 반환해준다.
const arr = [1, 2, 3, 4, 5];
const getDelay = (v, time) => new Promise((resolve) => setTimeout(() => resolve(v), time));
const cbFn = async (acc, v) => {
const res = await getDelay(v, 1000);
log('acc+res:', acc + res);
return acc + res;
};
const reduceAsync = async (cbFn, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const x of iter) {
acc = await cbFn(acc, x);
}
return acc;
};
const print = async () => {
const sum = await reduceAsync(cbFn, arr);
log('sum: ', sum);
};
print();
결과 : 1초간격으로 잘나온다!
acc+res: 3
acc+res: 6
acc+res: 10
acc+res: 15
sum: 15