ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • async /await 에 대하여
    네트워크/CS Study 2023. 6. 14. 23:14

    1. 사용하는 이유

    비동기 처리를 하는 가장 기본적인 call back 함수를 이용한 처리와 promise 객체를 이용한 처리가 단점이 많다.

    promise 객체를 더욱 더 가시성 좋게 사용하기 위해 사용합니다

     

    promise와 callback의 단점

    1. 보기에 너무 나쁘다 call back 지옥 보면 토나오는 모양새이고 promise 또한 then으로 병렬적으로 쓰여진 것도 에러가 발생했을 때 찾기 좀 힘들고 들여쓰기가 많이 들어간다면 정신건강에도 나쁘다.

    2. catch() 메서드를 사용해서 에러에 대해서 예외처리를 할 때 동기코드와 비동기코드가 섞여 있으면 누락이 있거나 난해해지는 경우가 있다.

    그래서 async/await은 promise를 이용한 비동기처리의 불편함을 개선하기 위해서 ES7에서 새롭게 추가된 내용이다.

    2. 문법 형태

    var addCoffee = function (name) {
    	return new Promise(function (resolve) {
    		setTimeout(function(){
    			resolve(name);
    		}, 500);
    	});
    };
    var coffeeMaker = async function () {
    	var coffeeList = '';
    	var _addCoffee = async function (name) {
    		coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name);
    	};
    	await _addCoffee('에스프레소');
    	console.log(coffeeList);
    	await _addCoffee('아메리카노');
    };
    coffeeMaker();

    async 는 promise객체를 반환한다 만약 return 으로 promise 가 아닌 것을 반환을 한다해도 promise객체로 감싸서 반환을 시키기 때문에 then을 사용할 수 있다. await은 async문법 안에서만 작동을 하므로 그냥 사용할 수 없다.

    최상위 코드( 걍 맨 위) 에서는 작동하지 않는다. 작동하려면 ()로 살포시 감싸줘야한다.

     

    대충 promise 객체의 모양  이런갑다 생각을 하고 있자 promiseResult 안에 값이 들어간다는 것만 기억하면 됨

     

     

    아래의 결과 값은 같다 이걸 보고서 promise를 async /await 형태로 변환하는 것의 느낌을 잡아봐야겠다. 느낌을 잡았다면 예시에 나온 것도 한번 봐보자.

        async function foo() {
            await 1
        }
        function foo() {
            return Promise.resolve(1).then(() => undefined)
        }

     

    promise.all 을 통한 병렬처리가 가능하다 이건 밑에 링크 들어가서 자세히 보셈

    3.예시에 있는 예제를 통해서 더욱 개념을 단단히 잡아보자

    async function myFunction() {
      try {
        const [result1, result2] = await Promise.all([asyncOperation1(), asyncOperation2()]);
        console.log(result1, result2);
      } catch (error) {
        console.error(error);
      }
    }

     


    2. 동작원리

    비동기처리의 동작원리를 이해하려면 콜스택과 테스트큐를 라는 공간을 이해를 하면 쉽다.

    아래에 있는 로직의 작동 순서가 이해가 간다면 그냥 이 글 읽을 필요도 없음. (난 좀 충격적이였음)

    const a = () => {
      console.log("a 시작");
      b();
      console.log("a 끝");
    };
    
    const b = async () => {
      console.log("b 시작");
      await c();
      console.log("b 끝");
    };
    
    const c = async () => {
      console.log("c 시작");
      await d();
      console.log("c 끝");
    };
    
    const d = () => {
      console.log("d")
    };
    
    a();

    결과만 말하자면 순서는 a시작  > b시작 > c시작 > d시작 > a끝 > c끝 > b끝 이며,  콜스택에서 실행되던 와중에 await을 만나게 되면 그 함수는 일시정지하며 테스크큐로 이동하게 된다 그렇게 되면 일시정지 하지 않고 있는 a끝이 먼저 실행되고 콜스택 순서대로 일시정지했던 함수들이 실행되며 빠져나가게 된다 이 과정은 콜스택이 빈 순간 이벤트 루프가 테스크큐에서 함수를 다시 콜스택으로 이동시키기 때문에 일어나는 것임을 기억하고  콜스택, 이벤트루프, 테스크큐 를 통한 작동원리를 알아보았


    3. 예제

     예제문제는 mdn에서 가져 온 내용이다 병렬처리과정에 대한 내용도 들어가 있고 promise와 await의 차이를 잘 볼 수 있어서 이 로직을 잘 이해한다면 거의 다 이해한거라고 봐도 될 듯하다

        var resolveAfter2Seconds = function() {
          console.log("starting slow promise");
          return new Promise(resolve => {
            setTimeout(function() {
              resolve(20);
              console.log("slow promise is done");
            }, 2000);
          });
        };
    
        var resolveAfter1Second = function() {
          console.log("starting fast promise");
          return new Promise(resolve => {
            setTimeout(function() {
              resolve(10);
              console.log("fast promise is done");
            }, 1000);
          });
        };
    
        var sequentialStart = async function() {
          console.log('==SEQUENTIAL START==');
    
          // If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise.
          const slow = await resolveAfter2Seconds();
          console.log(slow);
    
          const fast = await resolveAfter1Second();
          console.log(fast);
        }
    
        var concurrentStart = async function() {
          console.log('==CONCURRENT START with await==');
          const slow = resolveAfter2Seconds(); // starts timer immediately
          const fast = resolveAfter1Second();
    
          console.log(await slow);
          console.log(await fast); // waits for slow to finish, even though fast is already done!
        }
    
        var stillConcurrent = function() {
          console.log('==CONCURRENT START with Promise.all==');
          Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
            console.log(messages[0]); // slow
            console.log(messages[1]); // fast
          });
        }
    
        var parallel = function() {
          console.log('==PARALLEL with Promise.then==');
          resolveAfter2Seconds().then((message)=>console.log(message));
          resolveAfter1Second().then((message)=>console.log(message));
        }
    
        sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"
        // wait above to finish
        setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"
        // wait again
        setTimeout(stillConcurrent, 7000); // same as concurrentStart
        // wait again
        setTimeout(parallel, 10000); // trully parallel: after 1 second, logs "fast", then after 1 more second, "slow"

    중요한 것은 await을 선언한 시점과 promise는 같은 결과를 어떻게 출력하는지에 대해서 잘 보면 된


    4. 단점

     

    에러핸들링에 try catch 구문이 필요하다 (근데 promise 에러핸들링하는거보다 보기 쉬운거 같다 개인적으로)

    함수의 결과 값이 promise로 반환된다는 것이 명시적으로 나타나있지 않아서 헷갈리면 안된다.


    5. 주변용어

     스레드 : 커피숍 생각하기(처리할 수 있는 카운터 갯수) js는 싱글스레드

     

    동기 / 비동기연산 : 커피숍 생각하기 (주문받고 음식나올 때까지 전담케어하면 동기, 진동벨 주고 빠른거 먼저 처리하면 비동기)

     

    promise : https://spicycookie.me/JavaScript/promise/

    promise 관련되서 궁금할 때에는 이거 보면 되겠다.

     

    제너레이터 (function*)  : 함수인데 중간에 원하는 부분에서 멈췄다가 그 부분부터 다 실행하는 함수

    https://seo-tory.tistory.com/77

    yeild를 비롯해서 여러가지 개념이 많이 나오네....

     

    thenable 객체 : then 메소드를 갖는 객체를 뜻한다. 체이닝이나 await과 같은 promise 패턴을 가진 구문에서 사용할 수 있다. 모든 promise객체는 thenable 객체이지만 역은 성립하지 않음.

     

    promise.then : 두개의 콜백함수를 매개변수로 받는데 각각 resolve 와 reject로 생각하면 된다.

    p.then(onFulfilled, onRejected);
    
    p.then(function(value) {
      // 이행
    }, function(reason) {
      // 거부
    });


    스레드 (≒task)
    어떤 프로그램 내에서(프로세스) 실행되는 흐름의 단위 일반적으로 한프로그램에 한개 스레드

     

    fetch 함수 :  fetch함수는 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API

     

    latency : 지연시간

     

     

     

     

    참고자료:

    https://www.daleseo.com/js-async-async-await/

    https://sangminem.tistory.com/284

    https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/async_function#simple_example

    https://velog.io/@jjunyjjuny/JavaScript-asyncawait%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%A0%EA%B9%8C

    https://nayoungkim00.tistory.com/43

     

    📚 자바스크립트의 비동기 처리 동작 원리 | 콜 스택, 이벤트루프, 콜백헬, Promise, fetch vs axios, asy

    목차 1. 비동기처리의 필요성 1-1. 실행컨텍스트 스택 1-2. 싱글스레드 2. 비동기처리의 동작 원리 2-1. 브라우저의 이벤트 루프 2-2. 왜 싱글스레드일까? 3. 콜백 비동기 처리의 문제점 3-1. 콜백 헬 (Ca

    nayoungkim00.tistory.com

    https://ko.javascript.info/async-await

    댓글

Designed by Tistory.