[JS]forEach의 콜백함수에서는 async함수를 사용할 수 없다??

Kyle·2021년 3월 5일
1

javascript

목록 보기
14/18

forEach의 콜백함수에서는 async함수를 사용할 수 없다. 반면 for 또는 for...of에서는 사용가능하다. 그 이유가 무엇일까??

이유를 알아보기전 for 또는 for...of는 어떻게 async를 적용시키는지 간단하게만 보고 넘어가자.

for...of에 적용해보기 (일반 for문도 똑같이 적용된다.)

for...of문은 아래와 같이 간단하게 async, await를 적용시킬 수 있다.

const loopAsync = async(fn) =>{
  for(const x of arr){
    const res = await fn(x)
    console.log(res)
  }
}

반면에 forEach는 안된다. 그 이유를 알아보자.

forEach의 콜백함수에 async await 함수를 사용할 수 없는 이유??

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

비동기적으로 실행시켜주는 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

출처

profile
Kyle 발전기

0개의 댓글