[React #11] Promise / async / await

    반응형

    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) : 기초부터 실전까지'
    반응형

    댓글