Step by Step

비동기(Callback, Promise, Await, Async) 본문

Javascript Study

비동기(Callback, Promise, Await, Async)

짤진이 2023. 9. 4. 00:07
반응형

Callback 함수는 이해하기가 정말 까다로웠다

함수 안에 함수를 호출하는데 작동 순서, 원리등을 이해하는데 꽤 오랜 시간이 걸렸다.

두번째 예시는 주로 강의에서 설명하던 방식인데 함수안에 저런 형태의 함수는 한번에 이해하기 힘들어서 첫번째 예시로 다시 만들어서 생각해보니 이해하기 훨씬 쉬웠다.

 

const add = (a, b, cb) => {
  return cb(a + b);
};

const say = (val) => {
  return console.log(val);
};

add(3, 4, say);

 

add 함수 안에 a,b,cb 인자를 받아주었는데 cb는 함수 인자이다.

작동 순서는 add(3,4,say)로 예시를 들면 3,4의 합을 say함수 인자로 받는다.

그 후 say 함수에서 console.log(val)로 console창에 출력을 해준다.

 

const add = (a, b, cb) => {
  return cb(a + b);
};

add(3, 4, (val) => console.log(val));

 

쉽게 설명하면 (val) => console.log(val) 이 부분이 cb로 교체된다고 보면 된다.

return console.log(a+b) 이런 형식으로 바뀐다고 생각하니 이해가 쉬웠다.

 

Callback에서는 Callback 지옥이라는 표현이 있다.

아래 예시처럼 비동기적으로 코드를 적었을 때 함수 a,b,c,d 순서대로 호출하기 위해서는 a 함수를 실행한 후 callback으로 b를 받고 순차적으로 c,d를 받아 호출하는 경우가 있는데 이는 들여쓰기를 반복해서 가시성이 떨어진다는 단점이 있다.

 

const a = (callback) => {
  setTimeout(() => {
    console.log(1);
    callback();
  }, 1000);
};
const b = (callback) => {
  setTimeout(() => {
    console.log(2);
    callback();
  }, 1000);
};
const c = (callback) => {
  setTimeout(() => {
    console.log(3);
    callback();
  }, 1000);
};
const d = () => console.log(4);
a(() => {
  b(() => {
    c(() => {
      d();
    });
  });
});

 

이런 단점을 고치기 위해 Promise 패턴을 사용하는데 callback 인수를 제거한 후 밑부분에 Promise를 선언한 후 인자값으로 resolve를 받는다. console.log(1) 부분 다음 줄에 resolve();를 삽입해 저 부분에서 다음 함수를 .then()문법을 사용해서 메소드 체이닝하는 것이다.

 

const a = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(1);
      resolve();
    }, 1000);
  });
};

const b = () => console.log(2);

a().then(() => {
  b();
});

const a = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(1);
      resolve();
    }, 1000);
  });
};
const b = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 1000);
  });
};
const c = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(3);
      resolve();
    }, 1000);
  });
};
const d = () => console.log(4);
//메소드 체이닝
a()
  .then(() => b())
  .then(() => c())
  .then(() => d())
  .then(() => console.log("done"));

 

위의 Callback, Promise 패턴보다 좀 더 간결한 문법이 있는데 이는 Await,Async이다.

똑같이 Promise를 사용하지만 .then()을 사용하지 않고 새로운 wrap함수는 만든 후 async로 묶어준 다음 호출하려는 함수 앞에 await을 사용하면 a함수가 실행되는 것을 기다린 후 a함수의 resolve()에서 b()가 실행되어 순차적으로 1,2가 콘솔창에서 출력되는것을 확인할 수 있다.

const a = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(1);
      resolve();
    }, 1000);
  });
};

const b = () => console.log(2);

// a().then(() => {
//   return b()
// })

const wrap = async () => {
  await a(); //resolve 기다렸다가
  b();
};
wrap();

 

Resolve, Rejcet 그리고 에러 핸들링을 알아보자.

아래 코드는 Promise를 사용하여 index 값이 10이하면 console.log(index), resolve(index+1) 코드가 실행된다.

delayAdd에 인자값 10일 넣어주면 10보다 크지 않으므로 출력값은 10, 11이 된다.

하지만 10보다 큰 13을 넣어주게 되면 if 조건문에서 조건을 만족하므로 reject문이 실행되어 '13는 10보다 클 수 없습니다' 라는 에러 메세지가 콘솔창에 나타나는 것을 확인할 수 있다.

 

delay(13).then().catch()문은 then에서 resolve문에 맞는 함수가 연결되고 catch에서 reject문에 맞는 함수가 연결된다.

 

const delayAdd = (index) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (index > 10) {
        reject(`${index}는 10보다 클 수 없습니다`);
        return;
      }
      console.log(index);
      resolve(index + 1);
    }, 1000);
  });
};

delayAdd(13)
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

 

Await, Async문을 활용할 수도 있다.

then()을 사용하는 대신 try문을 사용해서 await을 resolve와 연결할 수도 있다.

 

const delayAdd = (index) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (index > 10) {
        reject(`${index}는 10보다 클 수 없습니다`);
        return;
      }
      console.log(index);
      resolve(index + 1);
    }, 1000);
  });
};

delayAdd(3)
  .then((res) => console.log(res))
  .catch((err) => console.error(err))
  .finally(() => console.log("done"));

const wrap = async () => {
  try {
    const res = await delayAdd(3);
    console.log(res);
  } catch (err) {
    console.error(err);
  } finally {
    console.log("done");
  }
};

wrap();

 

강의를 들으면서 이해가 안되는 부분은 구조를 먼저 파악해 생각해보니 이해가 수월하게 되었던 것 같다.

반응형