React

[React #11] Promise / async / await

PEAZH 2024. 2. 28. 13:09
반응형

01. Promise

  • 자바스크립트의 비동기를 돕는 객체로, 콜백 지옥을 극복할 수 있음
  • 비동기를 쉽고 빠르고 직관적이게 만들 수 있음
  • 비동기 처리의 결과값을 핸들링하는 코드를 비동기 함수로부터 분리할 수 있음
  • 프로미스를 사용하면 더이상 비동기 처리 함수에 콜백을 줄지어 전달할 필요가 없음

 

💡콜백 지옥이란?

더보기
  • 연속되는 비동기 함수들을 처리할 때 비동기 처리의 결과 값을 사용하기 위해 콜백이 계속 깊어지는 현상
  • 비동기 처리의 결과를 또 다른 비동기 처리의 결과로 이용하는 로직이 계속 길어지는 현상
step1(function (value1) {
  step2(function (value2) {
    step3(function (value3) {
      step4(function (value4) {
        step5(function (value5) {
          step6(function (value6) {
            step7(function (value) {
              consolo.log(value7);
            });
          });
        });
      });
    });
  });
});

 

✏️ promise의 예시

function isPositiveP(number) {
  const executor = (resolve, reject) => {// 실행자
    setTimeout(() => {
      // 자료형이 숫자일 때만 작업하기기
      if (typeof number === "number") {
        // 성공했을 때 (resolve)
        console.log(number);
      } else {
        // 실패했을 때 (reject)
      }
    }, 2000);
  };
  const asyncTask = new Promise(executor);
  return asyncTask;
}
isPositiveP(101);

// 101

 

 

📑 비동기 작업을 실질적으로 수행하는 executor의 함수를 실행시키는 방법 

const asyncTask = new Promise(executor);
return asyncTask;

1) 비동기 작업 자체인 promise를 저장할 asyncTask를 만듦

2) new 키워드를 사용해서 promise 객체를 생성하면서 생성자로 executor를 넘겨줌

3) 넘겨주면 전달하는 순간 자동으로 executor 함수가 바로 실행이 됨

4) executor를 담은 promise 객체를 asyncTask라는 상수에 담았으므로 이 상수를 return 해줌

 

isPositiveP 함수의 반환 값은 Promise

* 비동기 작업을 하고 그 결과를 promise 객체로 반환 받아서 비동기 처리의 결과값을 사용할 수 있음

 

 

 

✏️ promise로 받은 객체 사용하기

const res = isPositiveP([]);
res
  .then((res) => {
    console.log("작업 성공 : ", res);
  })
  .catch((err) => {
    console.log("작업실패 : ", err);
  });
  • 반환한 프로미스 객체를 res라는 상수에 저장하기
  • res는 반환받은 프로미스 객체를 이용해서 비동기 처리에 대한 resolve, reject의 결과값을 아무데서나 사용할 수 있음
  • 프로미스 객체의 메서드인 then과 catch를 사용
  • 첫 번째로 전달받은 인자는 then에서, 두 번째로 전달받은 인자는 catch에서 결과 값을 받을 수 있음

 

 

✏️ Callback과 Promise의 비교

 

1) Callback

function taskA(a, b, cb) {
  setTimeout(() => {
    const res = a + b;
    cb(res);
  }, 3000);
}
function taskB(a, cb) {
  setTimeout(() => {
    const res = a * 2;
    cb(res);
  }, 1000);
}
function taskC(a, cb) {
  setTimeout(() => {
    const res = a * -1;
    cb(res);
  }, 2000);
}

taskA(3, 4, (a_res) => {
  console.log("taskA :", a_res);
  taskB(a_res, (b_res) => {
    console.log("taskB :", b_res);
    taskC(b_res, (c_res) => {
      console.log("taskC :", c_res);
    });
  });
});

 

2) Promise

function taskA(a, b) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const res = a + b;
      resolve(res);
    }, 3000);
  });
}

function taskB(a) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const res = a * 2;
      resolve(res);
    }, 1000);
  });
}
function taskC(a) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const res = a * -1;
      resolve(res);
    }, 2000);
  });
}

const bPromiseResult = taskA(1, 3).then((a_res) => {
  console.log("taskA :", a_res);
  return taskB(a_res);
});
console.log("block");
bPromiseResult
  .then((b_res) => {
    console.log("taskB :", b_res);
    return taskC(b_res);
  })
  .then((c_res) => {
    console.log("taskC :", c_res);
  });
  • then 체이닝 : then 메서드를 계속 이어 붙이는 것
  • taskA의 결과값인 a_res를 taskB에 인자로 전달하고 then 메서드를 이용하여 그 결과값인 b_res를 taskC의 인자로 전달하는 형태

프로미스와 콜백의 비교

  • 프로미스는 코드를 계속 아래로 늘여쓸 수 있고 중간에 다른 작업을 끼어넣을 수도 있음
  • 프로미스 객체를 이용하면 비동기 처리를 호출 코드와 결과 코드를 분리해서 사용할 수 있음
  • 프로미스는 좀 더 가독성 있고 깔끔한 비동기 처리가 가능함

 

02. async / await

  • 비동기를 다루는 기능으로, promise를 더 쉽고 가독성 좋게 작성할 수 있음

 

1) async

- 함수 앞 부분에 'async'라고 붙이면 자동으로 프로미스를 리턴하는 비동기 처리 함수가 됨

- 프로미스를 반환한다는 것은 호출할 필요가 없고 then을 쓸 수 있다는 뜻

- async를 붙여준 함수는 프로미스의 resolve 값을 리턴함

promise를 리턴한다는 것을 확인할 수 있음

✏️ async의 예시 코드

function hello() {
  return "hello";
}

async function helloAsync() {
  return "hello Async";
}

console.log(hello()); // hello
console.log(helloAsync()); // Promise {} (객체 자체)

helloAsync().then((res) => {
  console.log(res); // hello Async
});

 

2) await

- await는 async 키워드가 붙은 함수 내에서만 사용 가능함

- await 함수를 비동기 함수의 호출 앞에 붙이게 되면 비동기 함수가 마치 동기적인 함수처럼 작동을 함

- await 키워드가 붙은 함수의 호출은 뒤에 있는 함수가 끝나기 전까지 아래 있는 코드를 수행하지 않음

 

✏️ await의 예시 코드

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function helloAsync() {
  await delay(3000);
  return "hello async";
}

async function main() {
  const res = await helloAsync();
  console.log(res);
}

main();

 

① resolve에 콜백 함수 자체를 전달 ✅

더보기

setTimeout 콜백 함수 안에 resolve() 호출 말고 다른게 없다면 콜백 함수 자체로 전달해도 됨

 

- 변경 전

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, ms);
  });
}

 

- 변경 후

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

 

② await 사용 ✅

더보기

- 변경 전

async function helloAsync() {
  return delay(3000).then(() => {
    return "hello Async";
  });
}

 

- 변경 후

async function helloAsync() {
  await delay(3000);
  return "hello async";
}

 

③ main 함수 안에서 await를 사용하여 호출하기 ✅

더보기

- 변경 전

helloAsync().then((res) => {
  console.log(res);
});

 

- 변경 후

async function main() {
  const res = await helloAsync();
  console.log(res);
}

main();

 

 

참고자료

이정환 Winterlood, '한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지'
반응형