콜백 지옥과 비동기 제어

콜백지옥

  • 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기가 감당이 힘들 정도로 깊어지는 현상

      const accessToken = "token";
      function fetchMyOrders() {
        checkNetworkStatus().then(status => 
          if (status) 
            loginCheck(accessToken).then(user => {
              setUser(user);
              fetchOrdersById(user.userId).then(orders => {
                setOrders(orders);
              });
            })
        );
      }
    

콜백 지옥과 비동기 제어

동기와 비동기

  • 현재 실행 중인 코드가 완료된 후에야 다음 코드를 실행되는 코드는 동기
  • 특정 시간이 경과되면, 사용자의 직접적 개입이 발생하면, 브라우저 외의 대상에 요청하고 응답이 왔을 때 등 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 비동기

콜백지옥 해결

  1. 기명함수로 변환
     const accessToken = "token";
     function fetchMyOrders() {
       checkNetworkStatus().then(handleNetworkStatus);
     }
     function handleNetworkStatus(status) {
       if (status) loginCheck(accessToken).then(handleLogin);
     }
     function handleLogin(user) {
       setUser(user);
       fetchOrdersById(user.userId).then(handleOrder);
     }
     function handleOrder(orders) {
       setOrders(orders);
     }
    

콜백지옥 해결

  1. 비동기 작업의 동기적 표현(1) - Promise(1)

     new Promise(function (resolve) {
       checkNetworkStatus().then(status => {
         resolve(status)
       });
     }).then(function (status) {
       return new Promise(function (resolve) {
         if (status)
           loginCheck(accessToken).then(user => {
             setUser(user);
             resolve(user);
           })
       });
     }).then(function (user) {
       return new Promise(function (resolve) {
         fetchOrdersById(user.userId).then(orders => {
           setOrders(orders);
           resolve(orders);
         })
       });
     })
    

ES6 Promise는 new 연산자와 함께 호출한 Promise의 인자로 넘겨주는 콜백 함수는 바로 실행되고, resolve 또는 reject 가 호출되면 Promise의 then 또는 catch 구문으로 넘어감 ⇒ 비동기 작업의 동기적 표현 가능

  1. 비동기 작업의 동기적 표현(2) - Promise(2)

     var networkRequest = function (reqFunc, handleResult) {
       return function (prevResult) {
         return new Promise(function(resolve){
           reqFunc(prevResult).then((res) => resolve(handleResult ? handleResult(res) : res))
         })
       }
     }
     networkRequest(checkNetworkStatus)()
       .then(networkRequest(loginCheck, (user) => {
         setUser(user);
         return user;
       }))
       .then(networkRequest(fetchOrdersById, (orders) => {
         setOrders(orders);
         return orders;
       }))
    
  2. 비동기 작업의 동기적 표현(3) - Generator

     var request = function(params, reqFunc) {
       reqFunc(params).then((res) => requests.next(res));
     }
     var requestGenerator = function* () {
       var status = yield request(null, checkNetworkStatus);
       var user = yield request(accessToken, loginCheck);
       setUser(user);
       var orders = yield request(user.userId, fetchOrdersById);
       setOrders(orders);
     }
     var requests = requestGenerator();
     requests.next();
    

ES6의 Generator를 이용. * 이 붙은 함수가 Generator 함수로, 제너레이터 함수를 호출하면 next 메소드를 갖고 있는 Iterator 를반환. next 메소드를 호출하면 가장 먼저 등장하는 yield 에서 실행을 멈추고 다시 next 가 호출되면 다시 시작, 다음 yield 에서 멈춤. 이러한 방식으로 Generator 내부 코드가 위→아래로 순차적으로 실행

  1. 비동기 작업의 동기적 표현 (4) - Promise + async/await (편안..)
var requestBuilder = async function () {
    var status = await checkNetworkStatus();
    var user = await loginCheck(accessToken);
    setUser(user);
    var orders = await fetchOrdersById(user.userId);
    setOrders(orders);
}

ES2017부터 async/await 을 이용, 비동기 작업을 수행하고자 하는 함수 앞에 async 키워드를 붙이고, 실질적 비동기 작업을 요하는 곳에 await 을 붙이면 뒤의 내용을 Promise로 자동 전환 및 resolve 된 후에 다음으로 진행되게 함.

results matching ""

    No results matching ""