노트 #35 | 비동기 (Asynchronous)

HyeonWooGa·2022년 7월 26일
0

노트

목록 보기
36/74

개요

백그라운드 실행, 로딩 창, 서버요청/응답, 큰 용량의 파일 로딩 작업 등은 비동기적(non-blocking)으로 작동되어야 효율적입니다.


학습 목표

  • 어떤 경우에 중첩된 콜백(callback)이 발생되는지 이해할 수 있다.
  • 중첩된 콜백(callback)의 단점, Promise의 장점을 이해할 수 있다.
  • async/await 키워드에 대해 이해하고, 작동 원리를 이해할 수 있다.

콜백 (callback) 복습

  • callback 개요
    • 다른 함수의 전달인자(argument)로 넘겨주는 함수

      • parameter를 넘겨받는 함수는 callback 함수를 필요에 따라 즉시 실행(synchronously)할수도 있고 아니면 나중에 실행(asynchronously) 할수도 있습니다.
    • 예시

      // callback 예시
      
      function B() {
        console.log('called at the back!');
      }
      
      function A(callback) {
        callback(); // callback === B
      }
      
      A(B);

  • callback in action : iterator
    • 반복 실행하는 함수

    • 예시

      // callback in action : iterator
      
      [1,2,3].map(function(element, indx) {
        return element * element;
      });

  • callback in action : event handler
    • 이벤트에 따른 함수

    • 예시

      // callback in action : event handler
      
      document.querySelector('#btn').addEventListner('click', function(e) {
        console.log('button clicked');
      });
      

  • 주의점
    • 콜백함수 사용시에 실행을 시켜주면 안됩니다.

Blocking vs Non-Blocking

  • 전화
    • 하던 일을 멈추고 받아야 한다 (Blocking)
    • 요청에 대한 결과가 동시에 일어난다 (Synchronous)
  • 문자
    • 확인 후 나중에 답장할 수 있다 (Non-Blocking)
    • 요청에 대한 결과가 동시에 일어나지 않는다 (Asynchronous)

동기 vs 비동기

  • 커피 가게에서 알아보는 동기와 비동기
    • 동기인 경우
      • 커피를 주문하고 커피가 나올때까지 다음 주문이 Blocking 되어 있습니다.

      • 예시

        // 동기인 경우
        
        function waitSync(ms) {
          var start = Date.now();
          var now = start;
          while(now - start < ms) {
            now = Date.now();
          }
        } // 현재 시각과 시작 시간을 비교하며 ms 범위 내에서 무한 루프를 도는 blocking 함수입니다
        
        function drink(person, coffee) {
          console.log(person + '는 ' + coffee + '를 마십니다');
        }
        
        function orderCoffeeSync(coffee) {
          console.log(coffee + '가 접수되었습니다');
          waitSync(4000);
          return coffee;
        ]
        
        let customers = [{
          name: 'Steve',
          request: '카페라떼'
        }, {
          name: 'John',
          request: '아메리카노',
        }];
        
        //call synchronously
        
        customers.forEach(function(customer) {
          let coffee = orderCoffeeSync(customer.request);
          drink(customer.name, coffee);
        }
        
        // 카페라떼가 접수되었습니다
        // (4초후)
        // Steve는 카페라떼를 마십니다
        // 아메리카노가 접수되었습니다

    • 비동기인 경우
      • 커피를 주문하고 커피가 나올때까지 다음 주문이 Non-Blocking 되어 있습니다.

      • 예시

        // 비동기인 경우
        
        function waitASync(callback, ms) {
          setTimeout(callback, ms);
        } // 특정 시간 이후에 callback 함수가 실행되게끔 하는 브라우저 내장 기능입니다
        
        function drink(person, coffee) {
          console.log(person + '는 ' + coffee + '를 마십니다');
        }
        
        function orderCoffeeASync(coffee) {
          console.log(coffee + '가 접수되었습니다');
          waitAsync(function() {
            callback(menu);
          }, 4000);
        ]
        
        let customers = [{
          name: 'Steve',
          request: '카페라떼'
        }, {
          name: 'John',
          request: '아메리카노',
        }];
        
        //call asynchronously
        
        customers.forEach(function(customer) {
          orderCoffeeASync(customer.request, function(coffee) {
            drink(customer.name, coffee);  
          });
        }
        
        // 카페라떼가 접수되었습니다
        // 아메리카노가 접수되었습니다
        // (4초후)
        // Steve는 카페라떼를 마십니다
        // John는 아메리카노를 마십니다
      • 비동기 함수 전달 패턴 1: 콜백 패턴

        • 예시

          // 콜백 패턴
          
          let request = 'caffelatte';
          orderCoffeeAsync(request, function(response) {
            // response -> 주문한 커피 결과
            drink(response);
          };
      • 비동기 함수 전달 패턴 2: 이벤트 등록 패턴

        • 예시

          // 콜백 패턴
          
          let request = 'caffelatte';
          orderCoffeeAsync(request).onready = function(response) {
            // response -> 주문한 커피 결과
            drink(response);
          };

비동기의 주요 사례

  • DOM Element 이벤트 핸들러
    • 마우스, 키보드 입력 (click, keydown 등)
    • 페이지 로딩 (DOMContentLoaded 등)
  • 타이머
    • 타이머 API (setTimeout 등)
    • 애니메이션 API (requestAnimationFrame)
  • 서버에 자원 요청 및 응답
    • fetch API
    • AJAX (XHR)

Why Async

  • 만약 유튜브가 동기(Sync) 라면 영상을 보려고 요청한 후에 로딩이 완료되기 전까지 나갈 수도 없고 다른 작업을 전혀 못하고 기다리기만 해야합니다.


Callback

  • Async 가 좋은 것은 알겠는데 요청의 순서를 정할땐 어떻게 해야할까요?
    • Callback 을 사용합니다.
      • '하던일 다 끝나면 전화해줘' 같은 의미
      • 예시
// 순서를 제어하지 못하는 경우 (콜백 미사용)

const printString = (string) => {
    setTimeout(
        () => {
            console.log(string)
        },
        Math.floor(Math.random() * 100) + 1
    )
}

const printAll = () => {
    printString('A');
    printString('B');
    printString('C');
}
printAll(); // 'A', 'B', 'C' 가 뒤죽 순서 뒤죽박줄으로 출력

// 순서를 제어하는 경우 (콜백 사용)

const printString = (string, callback) => {
    setTimeout(
        () => {
            console.log(string)
          	callback()
        },
        Math.floor(Math.random() * 100) + 1
    )
}

const printAll = () => {
  printString('A', () => {
    printString('B', () => {
      printString('C', () => {})
    })
  })
}
printAll(); // 'A', 'B', 'C' 가 순서대로 출력
  • 콜백 에러 핸들링 디자인 (Callback error handling Design)
// 에러 핸들링 디자인 (전달인자는 callback 함수)

const somethingGonnaHappen = callback => {
  waitingUntilSomethingHappens()
  
  if (isSomethingGood) {
    callback(null, something)
  }
  
  if (ifSomethingBad) {
    callback(something, null)
  }
}

// 사용

somethingGonnaHappen((err, data) => {
  if (err) {
    console.log('ERR!!');
    return;
  }
  return data;
})
  • 다만 콜백이 너무 많은 경우 콜백 지옥에 빠지기 때문에 Promise, async/await 에 대해 학습해야 합니다.

Promise

  • Promise 는 클래스 입니다.
  • return new Promise((resolve, reject) => {...}, .then 사용
  • 예시
// 콜백 -> Promise

const printString = (string) => {
  return new Promise((resolve, reject) => {
    
    setTimeout(
        () => {
            console.log(string)
          	resolve()
        },
        Math.floor(Math.random() * 100) + 1
    )
  })
}

const printAll = () => {
  printString('A')
  .then(() => {
    return printString('B')
  })
  .then(() => {
    return printString('C')
  })
}
printAll(); // 'A', 'B', 'C' 가 순서대로 출력

  • 하지만 Promise 또한 프라미스 지옥에 빠질수도 있습니다. (return 처리를 적절히 못하는 경우)

  • 위의 Promise Chaining 예시와 같이 Promise 를 사용해줘야 합니다.

Async/Await

  • Promise 와 같지만 ES6 이후 나온 아주 좋은 키워드입니다.
  • Promise 의 'Syntatic Sugar' 즉, 문법적 설탕이라고 표현되는 키워드입니다.
  • 비동기 함수를 동기 함수 실행하는 것같이 간편하게 사용 가능합니다, async/await 키워드 사용시.
  • 사용 예시


참고

코드스테이츠 URClass

profile
Aim for the TOP, Developer

0개의 댓글