[functional JS ES6+] 비동기/동시성 프로그래밍 3 : async/await, Q&A
작성:    
업데이트:
카테고리: Functional JS
태그: FE Language, Functional JS, JS
본 포스트는 인프런의 함수형 프로그래밍과 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 등 지연 함수를 활용한다면, 에러 발생 부분 이전까지 평가되므로 에러 발생 회피 가능
- 반대로, 이를 허용하지 않고 엄격하게 작성하려면 즉시 평가로 전체 확인
댓글남기기