2-3. 컬렉션 중심 프로그래밍 - Promise, then, 동기/비동기 다형성, reduce에 Promise 다형성 추가
— Functional programming — 1 min read
프로그래머스에서 진행한 유인동님의 ES6로 알아보는 동시성 & 함수형 프로그래밍 강의를 들으며 정리한 내용입니다.
# Promise의 중요성
- 비동기 통신에서의 데이터 처리뿐만아니라
- UI 구현에서도 중요함
- confirm창을 띄우고, 확인을 눌러야 밑으로 내려가지는 UI
- 카카오톡 채팅방에 친구를 초대할 때, 친구목록을 띄워서 선택한 목록을 프로미스로 받아서 초대요청
- 2가지 인터렉션이 동시에 일어날 때, 박스가 올라가며 서버에 API요청으로 받아온 그림을 그리는 경우 -> 버벅버벅 튕기지 않으려면 두가지 인터렉션을 프로미스로 받아놓고 실행해야함
Promise를 얼마나 잘 다루느냐가 관건이다 !
고로 모든 코드를 짤 때, 동기/비동기 상황을 모두 염두해놓고 짜도록 하자 (난이도 up)
# reduce(f, coll, acc)
인자의 다형성
coll로 들어온 인자가 프로미스일 때, Promise안에 있는 값을 .then()
으로 꺼낸 데이터는 모두 컬렉션이라는 가정하에 reduce함수를 개선 시켜보자
1. 임시방편 async...await
- coll로 Promise가 들어왔을 때 임시방편으로, 함수에
async
를 달아주고await coll
로 풀어진 프로미스 값을 사용할 수 있다 - 하지만, 함수에
async
키워드를 다는순간 비동기가 일어나서, 항상 Promise를 반환한다 - 반환된 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 다형성 추가
coll
인자가 프로미스인 경우 ->then()
- 보조함수가 프로미스인 경우 ->
recur()
- 최초의
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); //105then1(console.log)(Promise.resolve(10)); //106then2(console.log, 10); //107then2(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. 보조함수가 비동기일 경우 -> 재귀
재귀의 공식
- 재귀를 돌리고자 하는 구간을 함수로 싼다
- 즉시실행 한다. 처음 돌 때 사용할 값(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