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

작성:    

업데이트:

카테고리:

태그: , ,

본 포스트는 인프런의 함수형 프로그래밍과 JavaScript ES6+ 강의(링크)를 듣고 정리한 내용입니다.


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 등 지연 함수를 활용한다면, 에러 발생 부분 이전까지 평가되므로 에러 발생 회피 가능
  • 반대로, 이를 허용하지 않고 엄격하게 작성하려면 즉시 평가로 전체 확인

댓글남기기