logo
Search검색어를 포함하는 게시물들이 최신순으로 표시됩니다.
    Table of Contents
    [functional JS ES6+] 비동기/동시성 프로그래밍 3 : async/await, Q&A

    이미지 보기

    [functional JS ES6+] 비동기/동시성 프로그래밍 3 : async/await, Q&A

    • 22.06.09 작성

    • 읽는 데 7

    TOC

    async-await

    Promise를 return하는 함수에서 보다 동기적으로 코드를 작성하는 방법

    function delay(a) {
      return new Promise(resolve => setTimeout(() => resolve(a), 500));
    }
    
    async function f1() {
      const a = await delay(10);
      const b = await delay(5);
      log(a); // 10
    }
    
    f1();
    
    • async로 표시한 함수 내부에서 지연되는 함수 또는 작업의 앞에 await를 붙임
    • 쉽게 말하면 await는 Promise 객체를 가능한 순간에 값으로 평가
    • 해당 Promise를 반환하는 statement는 비동기적으로 값으로 평가될 때까지 지연
    • 위의 코드에서 await가 없다면 Promise 객체가 출력, await가 있다면 계산된 10이 출력

    주의사항 : async 함수는 Promise 객체를 return

    • 때문에 async 함수 내부에서 처리된 값을 함수 내부에서만 사용한다면 무관
    • 하지만 async 함수의 결과값(return값)을 외부에서 사용하게 된다면 .then을 통해 외부에서 return값을 처리

    Q. Array.prototype.map과 FxJS의 map 함수의 차이점

    그냥 내장함수 쓰면 안되는건가요.... 뭐가 다른 거죠?

    Promise에 대한 동기/비동기 처리가 내장함수와 다르다.


    동기/비동기 처리의 차이

    async function delayI(a) {
      return new Promise(resolve => setTimeout(() => resolve(a), 100));
    }
    
    function f2() {
      const list = [1, 2, 3, 4];
      const res = list.map(a => delayI(a * a));
      log(res); // [Promise, Promise, Promise, Promise]
    }
    
    f2();
    
    • map 자체에 async/await가 고려되지 않아 그 외부에서 조작해 씌워서 해보려해도 잘 되지 않는다.
    • res 자체가 Promise 객체가 아닌, Promise들을 담은 array이다.
    • 때문에 await로 Promise를 값으로 resolve할 수 없다.

    async function f3() {
      const list = [1, 2, 3, 4];
      const temp = map(a => delayI(a * a), list);
      log(temp); // Promise {<pending>}(resolve 전)
      const res = await temp;
      log(res); // [1, 4, 9, 16](resolve 후)
    }
    
    f3();
    

    출력과 반환의 문제

    async function f4() {
      const list = [1, 2, 3, 4];
      const res = await map(a => delayI(a * a), list);
      log(res); // [1, 4, 9, 16]
      return res;
    }
    
    log(f4()); // Promise {<pending>}
    
    • 함수 내부에서 res는 값으로 resolve되었는데, 이를 return하여 출력하면 Promise 객체!

    • async는 Promise 객체를 반환하므로 아래처럼 출력해야 한다.
    // 1번 방법
    f4().then(log);
    
    // 2번 방법
    (async () => {
      log(await f4());
    }) ();
    
    • Promise 객체를 처리하는 방식으로 함수 외부에서 Promise를 값으로 resolve해주어야 한다.

    Q. 비동기는 async/await으로 제어할 수 있는데 pipeline이 필요한 이유

    파이프라인의 목적은 명령형 프로그래밍을 하지 않고 안전하게 함수 합성을 하는 것

    • 동기/비동기는 파이프라인의 기능과 목적이 아니다.
    • 비동기 처리를 위한 async/await는 서로 다른 문제를 해결하기 위한 서로 다른 기술!!
    • 직렬/병렬을 바꾸거나 함수 순서를 바꿀 때도 pipeline은 함수의 위치만 바꾸면 되지만, 풀어쓴 코드는 for문의 구조 자체를 변경해야 할 수도 있음

    Q. async/await를 pipeline에 함께 사용 가능한지

    가능! async/await가 필요한 여러 연산 결과를 모아 또 async/await가 필요한 연산에 사용하는 등의 경우에 사용

    async function f52(list) {
      const r1 = await go(list,
        L.map(a => delayI(a * a)),
        L.filter(a => delayI(a % 2)),
        L.map(a => delayI(a + 1)),
        C.take(2),
        reduce((a, b) => delayI(a, b)));
    
      const r2 = await go(list,
        L.map(a => delayI(a * a)),
        L.filter(a => delayI(a % 2)),
        reduce((a, b) => delayI(a, b)));
    
      const r3 = await delayI(r1 + r2);
      return r3 + 10;
    }
    
    go(f52([1, 2, 3, 4, 5, 6, 7, 8]), a => log(a, 'f52'));
    

    Q. 동기 상황에서의 에러핸들링

    function f8(list) {
      return (list || [])
        .map(a => a + 10)
        .filter(a => a % 2)
        .slice(0, 2);
    }
    
    log(f7(null));
    
    • null이나 undefined가 인자로 전달되더라도 ||(or)을 통해서 기본값을 줄 수 있음
    • try catch 문을 활용해 오류가 발생하는 경우 오류를 return하지 않고 특정 값을 return할 수 있다.

    Q. 비동기 상황에서의 에러핸들링

    function f8(list) {
      try {
        return list
          .map(a => new Promise(resolve => {
            dlfjaf;
          }))
          .filter(a => a % 2)
          .slice(0, 2);
      } catch (e) {
        log(e);
        return [];
      }
    }
    log(f8(['0', '1', '2', '{']));
    
    • Promise 내부에서 잘못된 코드로 오류가 발생한 상황
    • catch 문에 도달하기도 전에 오류가 console에 찍히게 된다.
    • 그냥 비동기 상황에서의 핸들링이 어렵다!!

    pipeline의 활용

    async function f9(list) {
      try {
        return await go(
          list,
          map(a => new Promise(resolve => {
            resolve(JSON.parse(a));
          })),
          filter(a => a % 2),
          take(2));
      } catch (e) {
        log(e, '--------------------------');
        return [];
      }
    }
    
    log(f9(['0', '1', '2', '{']).then(a => log(a, 'f9')));
    
    • go/pipeline 앞에 await를 붙여준다.

    • pipeline 자체의 반환값이 Promise.reject 객체가 되어 catch 문에 도달 가능

    • try 문 안쪽의 결과값이 Promise여야 한다.

    • pipeline의 함수 결합이 동기/비동기, Promise 처리가 확실하게 되어있어야 가능

    • 함수가 map, filter 대신 L.map, L.filter 등 지연 함수를 활용한다면, 에러 발생 부분 이전까지 평가되므로 에러 발생 회피 가능

    • 반대로, 이를 허용하지 않고 엄격하게 작성하려면 즉시 평가로 전체 확인

    profile

    FE Developer 박승훈

    노력하는 자는 즐기는 자를 이길 수 없다