Skip to content
Underbleu
GithubLinkedin

2-3. 컬렉션 중심 프로그래밍 - Promise, then, 동기/비동기 다형성, reduce에 Promise 다형성 추가

Functional programming1 min read

프로그래머스에서 진행한 유인동님의 ES6로 알아보는 동시성 & 함수형 프로그래밍 강의를 들으며 정리한 내용입니다.

# Promise의 중요성

  • 비동기 통신에서의 데이터 처리뿐만아니라
  • UI 구현에서도 중요함
    • confirm창을 띄우고, 확인을 눌러야 밑으로 내려가지는 UI
    • 카카오톡 채팅방에 친구를 초대할 때, 친구목록을 띄워서 선택한 목록을 프로미스로 받아서 초대요청
    • 2가지 인터렉션이 동시에 일어날 때, 박스가 올라가며 서버에 API요청으로 받아온 그림을 그리는 경우 -> 버벅버벅 튕기지 않으려면 두가지 인터렉션을 프로미스로 받아놓고 실행해야함

Promise를 얼마나 잘 다루느냐가 관건이다 !
고로 모든 코드를 짤 때, 동기/비동기 상황을 모두 염두해놓고 짜도록 하자 (난이도 up)


# reduce(f, coll, acc) 인자의 다형성

coll로 들어온 인자가 프로미스일 때, Promise안에 있는 값을 .then()으로 꺼낸 데이터는 모두 컬렉션이라는 가정하에 reduce함수를 개선 시켜보자

1. 임시방편 async...await

  1. coll로 Promise가 들어왔을 때 임시방편으로, 함수에 async를 달아주고 await coll로 풀어진 프로미스 값을 사용할 수 있다
  2. 하지만, 함수에 async키워드를 다는순간 비동기가 일어나서, 항상 Promise를 반환한다
  3. 반환된 Promise를 풀어주기 위해 또 async...await를 사용한다

-> 이 방법은 끝이 없다. async...await 지옥이 펼쳐진다

1// 1. 임시방편
2async function reduce(f, coll, acc) {
3 var iter = collIter(await coll); // coll 풀어주기
4 acc = acc === undefined ? iter.next().value : acc;
5 for(const v of iter) {
6 acc = f(acc, v);
7 }
8 return acc;
9}
10
11// 2. async 함수는 무조건 Promise를 반환한다
12console.log( reduce((a, b) => a + b, [1,2,3,4]) ); // Promise {<pending>}
13
14// 3. async...await 지옥 시작
15(async function() {
16 console.log(
17 await reduce((a, b) => a + b, Promise.resolve([1,2,3,4]))
18 );
19})(); // 10

비동기처리는 성능적으로 비싼 일이다

동기와 비동기를 동시에 지원하기 위해 모든 것을 프로미스로 다루겠다는 생각
-> 콜스택이 비워지질 않음 -> 엄청느려짐

대부분의 요즘 라이브러리들이 동기여도 비동기로 다루는 코드가 많기 때문에, 내가 모르는 사이에 프로그램이 느려지곤 한다


2. 인자에 대한 Promise 다형성 추가

  1. coll인자가 프로미스인 경우 -> then()
  2. 보조함수가 프로미스인 경우 -> recur()
  3. 최초의 acc가 프로미스인 경우 -> then()

1. coll이 프로미스인 경우

then(): 전달 받는 인자가 Promise던 아니던, 똑같이 값을 리턴

1const then1 = f => a => a instanceof Promise ? a.then(f) : f(a);
2const then2 = (f, a) => a instanceof Promise ? a.then(f) : f(a);
3
4then1(console.log)(10); //10
5then1(console.log)(Promise.resolve(10)); //10
6then2(console.log, 10); //10
7then2(console.log, Promise.resolve(10)); //10

reduce()가 coll로 프로미스를 받을 수 있도록 개선

1function reduce(f, coll, acc) {
2 return then2(function(coll) { // --> 1.
3 const iter = collIter(coll);
4 acc = acc === undefined ? iter.next().value : acc;
5 for(const v of iter) {
6 acc = f(acc, v);
7 }
8 return acc;
9 }, coll);
10}
11
12reduce2((a, b) => a + b, Promise.resolve([1,2,3]))
13.then(console.log) // 6
14
15(async function() {
16 console.log(
17 // --> await: Promise의 값을 반환
18 await reduce2((a, b) => a + b, Promise.resolve([1,2,3]));
19 );
20})(); // 6

2. 보조함수가 비동기일 경우 -> 재귀

재귀의 공식

  1. 재귀를 돌리고자 하는 구간을 함수로 싼다
  2. 즉시실행 한다. 처음 돌 때 사용할 값(acc)를 넣어준다
1function reduce(f, coll, acc) {
2 return then2(function(coll) {
3 const iter = collIter(coll);
4 acc = acc === undefined ? iter.next().value : acc;
5 return function recur(acc) { // acc: then으로 풀어낸 값. 2번째 for루프 이후에 사용
6 for (const v of iter) {
7 acc = f(acc, v);
8 if (acc instanceof Promise) return acc.then(recur);
9 }
10 return acc;
11 } (acc); // acc: 1번째 for루프에 사용
12 }, coll);
13}

3. 최조의 acc가 프로미스인 경우

coll이 Promise였을 때 처럼, then()을 사용

1function reduce(f, coll, acc) {
2 return then2(function(coll) {
3 const iter = collIter(coll);
4 acc = acc === undefined ? iter.next().value : acc;
5 return then1(function recur(acc) { //--> then1(): acc가 프로미스인지 체크
6 for (const v of iter) {
7 acc = f(acc, v);
8 if (acc instanceof Promise) return acc.then(recur);
9 }
10 return acc;
11 })(acc);
12 }, coll);
13}
14
15reduce(
16 (a, b) => Promise.resolve(a + b),
17 Promise.resolve([1,2,3]),
18 Promise.resolve(10))
19 .then(console.log) //16