├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── functional-javascript-01.iml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml └── vcs.xml ├── 01. 함수형 자바스크립트 기본기 ├── 1.html └── 2.html ├── 02. ES6에서의 순회와 이터러블 이터레이터 프로토콜 └── index.html ├── 03. 제너레이터와 이터레이터 └── index.html ├── 04. map, filter, reduce ├── 1 │ └── 1. index.html └── 2 │ ├── html │ └── 2.html │ └── lib │ └── fx.js ├── 05. 코드를 값으로 다루어 표현력 높이기 └── 1 │ ├── html │ └── 1.html │ └── lib │ └── fx.js ├── 06. HTML 출력해보기 └── 1 │ ├── html │ └── 1.html │ └── lib │ └── fx.js ├── 07. 지연성 1, 8. 지연성 2 ├── 1 │ ├── html │ │ └── 1.html │ └── lib │ │ └── fx.js ├── 2 │ ├── html │ │ └── 2.html │ └── lib │ │ └── fx.js ├── 3 │ ├── html │ │ └── 1.html │ └── lib │ │ └── fx.js └── 4 │ ├── html │ ├── 1.html │ └── yield...md │ └── lib │ └── fx.js ├── 09. 비동기 동시성 프로그래밍 └── 1 │ ├── html │ ├── 1-1.html │ ├── 1-2.html │ └── 1-3.html │ └── lib │ └── fx.js ├── 10. 비동기 동시성 프로그래밍 2 └── 1 │ ├── html │ ├── 1-1.html │ └── 1-2.html │ └── lib │ └── fx.js ├── 11. 비동기 동시성 프로그래밍 3 └── 1 │ ├── html │ └── 1-1.html │ └── lib │ └── fx.js ├── README.md ├── fx.js ├── index.js └── test └── index.js /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 디폴트 무시된 파일 2 | /shelf/ 3 | /workspace.xml 4 | # 에디터 기반 HTTP 클라이언트 요청 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/functional-javascript-01.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /01. 함수형 자바스크립트 기본기/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 평가 4 | - 코드가 계산(Evaluation) 되어 값을 만드는 것 5 | 6 | # 일급 7 | - 값으로 다룰 수 있다. 8 | - 변수에 담을 수 있다. 9 | - 함수의 인자로 사용될 수 있다. 10 | - 함수의 결과로 사용될 수 있다. 11 | 12 | 18 | 19 | # 일급 함수 20 | - 함수를 값으로 다룰 수 있다. 21 | - 조합성과 추상화의 도구 22 | 23 | 39 | -------------------------------------------------------------------------------- /01. 함수형 자바스크립트 기본기/2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 일급 함수 4 | - 함수가 값으로 다뤄질 수 있다. 5 | 6 | # 고차 함수 7 | - 함수를 값으로 다루는 함수 8 | 9 | ## 함수를 인자로 받아서 실행하는 함수 10 | - apply1 11 | - times 12 | 13 | 42 | 43 | ## 함수를 만들어 리턴하는 함수 (클로저를 만들어 리턴하는 함수) 44 | - addMaker 45 | 46 | 63 | -------------------------------------------------------------------------------- /02. ES6에서의 순회와 이터러블 이터레이터 프로토콜/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 기존과 달라진 ES6에서의 리스트 순회 4 | - for i++ 5 | - for of 6 | 7 | 23 | 24 | ### Array를 통해 알아보기 25 | 26 | 42 | 43 | ### Set을 통해 알아보기 44 | 45 | 55 | 56 | ### Map을 통해 알아보기 57 | 58 | 66 | 67 | ## 이터러블/이터레이터 프로토콜 68 | - 이터러블: 이터레이터를 리턴하는 [Symbol.iterator]() 를 가진 값 69 | - 이터레이터: { value, done } 객체를 리턴하는 next() 를 가진 값 70 | - 이터러블/이터레이터 프로토콜: 이터러블을 for...of, 전개 연산자 등과 함께 동작하도록한 규약 71 | 72 | ### 사용자 정의 이터러블을 통해 알아보기 73 | 74 | 110 | 111 | ## 전개 연산자 112 | 113 | 119 | -------------------------------------------------------------------------------- /03. 제너레이터와 이터레이터/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 제너레이터/이터레이터 4 | - 제너레이터: 이터레이터이자 이터러블을 생성하는 함수 5 | 6 | 제너레이터를 통해 어떤 값도 순회하게 만들 수 있음 7 | 8 | 31 | 32 | # odds 33 | 34 | 85 | 86 | # for of, 전개 연산자, 구조 분해, 나머지 연산자 87 | 88 | 103 | -------------------------------------------------------------------------------- /04. map, filter, reduce/1/1. index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | # map 15 | 16 | 42 | 43 | # 이터러블 프로토콜을 따른 map의 다형성 44 | 45 | 81 | 82 | 83 | # filter 84 | 85 | 122 | 123 | # reduce 124 | 125 | 159 | 160 | 171 | -------------------------------------------------------------------------------- /04. map, filter, reduce/2/html/2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 28 | -------------------------------------------------------------------------------- /04. map, filter, reduce/2/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const map = (f, iter) => { 4 | let res = []; 5 | for (const a of iter) { 6 | res.push(f(a)); 7 | } 8 | return res; 9 | }; 10 | 11 | const filter = (f, iter) => { 12 | let res = []; 13 | for (const a of iter) { 14 | if (f(a)) res.push(a); 15 | } 16 | return res; 17 | }; 18 | 19 | const reduce = (f, acc, iter) => { 20 | if (!iter) { 21 | iter = acc[Symbol.iterator](); 22 | acc = iter.next().value; 23 | } 24 | for (const a of iter) { 25 | acc = f(acc, a); 26 | } 27 | return acc; 28 | }; 29 | -------------------------------------------------------------------------------- /05. 코드를 값으로 다루어 표현력 높이기/1/html/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | # 코드를 값으로 다루어 표현력 높이기 23 | 24 | ## go, pipe 25 | 26 | 49 | 50 | 85 | 86 | ## curry 87 | 88 | 102 | 103 | # 함수 조합으로 함수 만들기 104 | 105 | 140 | -------------------------------------------------------------------------------- /05. 코드를 값으로 다루어 표현력 높이기/1/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | /** 3 | * 함수를 받아서 함수를 return 4 | * 인자가 2개 이상이면 받아둔 함수를 즉시 실행하고 적다면 return한 이후에 인자를 합쳐서 실행 5 | * 6 | * 1. curry는 f라는 함수를 인자로 받아 새로운 함수를 반환하는 화살표 함수 7 | * 2. 첫번째 반환 함수: (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 8 | * 3. 인자 검사 및 호춣 9 | * 3-1. _.length가 참이면 f(a, ..._)를 호출해 즉시 결과를 반환 10 | * 3-2. 거짓이면 다시 curry(f)를 호출해 나머지 인자를 받아 f(a, ..._)를 호출 11 | */ 12 | 13 | const curry = f => 14 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 15 | 16 | const map = curry((f, iter) => { 17 | let res = []; 18 | for (const a of iter) { 19 | res.push(f(a)); 20 | } 21 | return res; 22 | }); 23 | 24 | const filter = curry((f, iter) => { 25 | let res = []; 26 | for (const a of iter) { 27 | if (f(a)) res.push(a); 28 | } 29 | return res; 30 | }); 31 | 32 | const reduce = curry((f, acc, iter) => { 33 | if (!iter) { 34 | iter = acc[Symbol.iterator](); 35 | acc = iter.next().value; 36 | } 37 | for (const a of iter) { 38 | acc = f(acc, a); 39 | } 40 | return acc; 41 | }); 42 | -------------------------------------------------------------------------------- /06. HTML 출력해보기/1/html/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML 출력해보기 - 장바구니 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /06. HTML 출력해보기/1/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const map = curry((f, iter) => { 7 | let res = []; 8 | for (const a of iter) { 9 | res.push(f(a)); 10 | } 11 | return res; 12 | }); 13 | 14 | const filter = curry((f, iter) => { 15 | let res = []; 16 | for (const a of iter) { 17 | if (f(a)) res.push(a); 18 | } 19 | return res; 20 | }); 21 | 22 | const reduce = curry((f, acc, iter) => { 23 | if (!iter) { 24 | iter = acc[Symbol.iterator](); 25 | acc = iter.next().value; 26 | } 27 | for (const a of iter) { 28 | acc = f(acc, a); 29 | } 30 | return acc; 31 | }); 32 | 33 | const go = (...args) => reduce((a, f) => f(a), args); 34 | 35 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 36 | -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/1/html/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## range 4 | 5 | 21 | 22 | ## 느긋한 L.range 23 | 24 | 37 | 38 | 49 | 50 | ## take 51 | 52 | 77 | 78 | -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/1/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const map = curry((f, iter) => { 7 | let res = []; 8 | for (const a of iter) { 9 | res.push(f(a)); 10 | } 11 | return res; 12 | }); 13 | 14 | const filter = curry((f, iter) => { 15 | let res = []; 16 | for (const a of iter) { 17 | if (f(a)) res.push(a); 18 | } 19 | return res; 20 | }); 21 | 22 | const reduce = curry((f, acc, iter) => { 23 | if (!iter) { 24 | iter = acc[Symbol.iterator](); 25 | acc = iter.next().value; 26 | } 27 | for (const a of iter) { 28 | acc = f(acc, a); 29 | } 30 | return acc; 31 | }); 32 | 33 | const go = (...args) => reduce((a, f) => f(a), args); 34 | 35 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/2/html/2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 이터러블 중심 프로그래밍에서의 지연 평가 (Lazy Evaluation) 4 | - 제때 계산법 5 | - 느긋한 계산법 6 | - 제너레이터/이터레이터 프로토콜을 기반으로 구현 7 | 8 | ### L.map 9 | 10 | 19 | 20 | ### L.filter 21 | 22 | 31 | 32 | 33 | ### range, map, filter, take, reduce 중첩 사용 34 | 35 | 102 | 103 | ### L.range, L.map, L.filter, take, reduce 중첩 사용 104 | 105 | 151 | 152 | ### map, filter 계열 함수들이 가지는 결합 법칙 153 | 154 | - 사용하는 데이터가 무엇이든지 155 | - 사용하는 보조 함수가 순수 함수라면 무엇이든지 156 | - 아래와 같이 결합한다면 둘 다 결과가 같다. 157 | 158 | [[mapping, mapping], [filtering, filtering], [mapping, mapping]] 159 | = 160 | [[mapping, filtering, mapping], [mapping, filtering, mapping]] 161 | 162 | 163 | ## 결과를 만드는 함수 reduce, take 164 | 165 | ### reduce 166 | 167 | 191 | 192 | ### take, find 193 | 194 | -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/2/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | // const map = curry((f, iter) => { 7 | // let res = []; 8 | // for (const a of iter) { 9 | // res.push(f(a)); 10 | // } 11 | // return res; 12 | // }); 13 | // 14 | // const filter = curry((f, iter) => { 15 | // let res = []; 16 | // for (const a of iter) { 17 | // if (f(a)) res.push(a); 18 | // } 19 | // return res; 20 | // }); 21 | 22 | // const reduce = curry((f, acc, iter) => { 23 | // if (!iter) { 24 | // iter = acc[Symbol.iterator](); 25 | // acc = iter.next().value; 26 | // } 27 | // for (const a of iter) { 28 | // acc = f(acc, a); 29 | // } 30 | // return acc; 31 | // }); 32 | 33 | const go = (...args) => reduce((a, f) => f(a), args); 34 | 35 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 36 | 37 | var add = (a, b) => a + b; 38 | 39 | // const range = l => { 40 | // let i = -1; 41 | // let res = []; 42 | // while (++i < l) { 43 | // res.push(i); 44 | // } 45 | // return res; 46 | // }; 47 | 48 | const L = {}; 49 | -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/3/html/1.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/3/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const L = {}; 7 | 8 | L.range = function* (l) { 9 | let i = -1; 10 | while (++i < l) { 11 | yield i; 12 | } 13 | }; 14 | 15 | L.entries = function* (obj) { 16 | for (const k in obj) yield [k, obj[k]]; 17 | }; 18 | 19 | const take = curry((l, iter) => { 20 | let res = []; 21 | iter = iter[Symbol.iterator](); 22 | let cur; 23 | while (!(cur = iter.next()).done) { 24 | const a = cur.value; 25 | res.push(a); 26 | if (res.length == l) return res; 27 | } 28 | return res; 29 | }); 30 | 31 | const reduce = curry((f, acc, iter) => { 32 | if (!iter) { 33 | iter = acc[Symbol.iterator](); 34 | acc = iter.next().value; 35 | } else { 36 | iter = iter[Symbol.iterator](); 37 | } 38 | let cur; 39 | while (!(cur = iter.next()).done) { 40 | const a = cur.value; 41 | acc = f(acc, a); 42 | } 43 | return acc; 44 | }); 45 | 46 | const find = curry((f, iter) => go( 47 | iter, 48 | L.filter(f), 49 | take(1), 50 | ([a]) => a)); 51 | 52 | const go = (...args) => reduce((a, f) => f(a), args); 53 | 54 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 55 | 56 | var add = (a, b) => a + b; 57 | 58 | const range = l => { 59 | let i = -1; 60 | let res = []; 61 | while (++i < l) { 62 | res.push(i); 63 | } 64 | return res; 65 | }; -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/4/html/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## L.flatten 4 | 5 | 33 | 34 | ## yield * 35 | 36 | 44 | 45 | `yield *`을 활용하면 위 코드를 아래와 같이 변경할 수 있습니다. `yield *iterable`은 `for (const val of iterable) yield val;` 과 같습니다. 46 | 47 | 55 | 56 | ## L.deepFlat 57 | 58 | 만일 깊은 Iterable을 모두 펼치고 싶다면 아래와 같이 `L.deepFlat`을 구현하여 사용할 수 있습니다. `L.deepFlat`은 깊은 Iterable을 펼쳐줍니다. 59 | 60 | 70 | 71 | ## L.flatMap 72 | 73 | 105 | 106 | 107 | ## 2차원 배열 다루기 108 | 109 | 125 | 126 | 127 | ## 지연성 / 이터러블 중심 프로그래밍 실무적인 코드 128 | 129 | 164 | 165 | -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/4/html/yield...md: -------------------------------------------------------------------------------- 1 | ## yield* 2 | 3 | ```javascript 4 | L.flatten = function* (iter) { 5 | for (const a of iter) { 6 | if (isIterable(a)) for (const b of a) yield b 7 | else yield a; 8 | } 9 | }; 10 | ``` 11 | 12 | `yield*`을 활용하면 위 코드를 아래와 같이 변경할 수 있습니다. `yield* iterable`은 `for (const val of iterable) yield val;` 과 같습니다. 13 | 14 | ```javascript 15 | L.flatten = function* (iter) { 16 | for (const a of iter) { 17 | if (isIterable(a)) yield* a; 18 | else yield a; 19 | } 20 | }; 21 | ``` 22 | 23 | ## L.deepFlat 24 | 25 | 만일 깊은 Iterable을 모두 펼치고 싶다면 아래와 같이 `L.deepFlat`을 구현하여 사용할 수 있습니다. `L.deepFlat`은 깊은 Iterable을 펼쳐줍니다. 26 | 27 | ```javascript 28 | L.deepFlat = function* f(iter) { 29 | for (const a of iter) { 30 | if (isIterable(a)) yield* f(a); 31 | else yield a; 32 | } 33 | }; 34 | log([...L.deepFlat([1, [2, [3, 4], [[5]]]])]); 35 | // [1, 2, 3, 4, 5]; 36 | ``` -------------------------------------------------------------------------------- /07. 지연성 1, 8. 지연성 2/4/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const reduce = curry((f, acc, iter) => { 7 | if (!iter) { 8 | iter = acc[Symbol.iterator](); 9 | acc = iter.next().value; 10 | } else { 11 | iter = iter[Symbol.iterator](); 12 | } 13 | let cur; 14 | while (!(cur = iter.next()).done) { 15 | const a = cur.value; 16 | acc = f(acc, a); 17 | } 18 | return acc; 19 | }); 20 | 21 | const go = (...args) => reduce((a, f) => f(a), args); 22 | 23 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 24 | 25 | const take = curry((l, iter) => { 26 | let res = []; 27 | iter = iter[Symbol.iterator](); 28 | let cur; 29 | while (!(cur = iter.next()).done) { 30 | const a = cur.value; 31 | res.push(a); 32 | if (res.length == l) return res; 33 | } 34 | return res; 35 | }); 36 | 37 | const takeAll = take(Infinity); 38 | 39 | const L = {}; 40 | 41 | L.range = function* (l) { 42 | let i = -1; 43 | while (++i < l) yield i; 44 | }; 45 | 46 | L.map = curry(function* (f, iter) { 47 | for (const a of iter) { 48 | yield f(a); 49 | } 50 | }); 51 | 52 | L.filter = curry(function* (f, iter) { 53 | for (const a of iter) { 54 | if (f(a)) yield a; 55 | } 56 | }); 57 | 58 | L.entries = function* (obj) { 59 | for (const k in obj) yield [k, obj[k]]; 60 | }; 61 | 62 | const map = curry(pipe(L.map, takeAll)); 63 | 64 | const filter = curry(pipe(L.filter, takeAll)); 65 | 66 | const find = curry((f, iter) => go( 67 | iter, 68 | L.filter(f), 69 | take(1), 70 | ([a]) => a)); 71 | 72 | var add = (a, b) => a + b; 73 | 74 | const range = l => { 75 | let i = -1; 76 | let res = []; 77 | while (++i < l) { 78 | res.push(i); 79 | } 80 | return res; 81 | }; -------------------------------------------------------------------------------- /09. 비동기 동시성 프로그래밍/1/html/1-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Promise 4 | 5 | ## 일급 6 | 7 | 33 | 34 | ## 일급 활용 35 | 36 | 49 | 50 | ## Composition 51 | 52 | 70 | 71 | ## Kleisli Composition 72 | 73 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /09. 비동기 동시성 프로그래밍/1/html/1-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## go, pipe, reduce에서 비동기 제어 4 | 5 | -------------------------------------------------------------------------------- /09. 비동기 동시성 프로그래밍/1/html/1-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## promise.then의 중요한 규칙 4 | 5 | -------------------------------------------------------------------------------- /09. 비동기 동시성 프로그래밍/1/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const isIterable = a => a && a[Symbol.iterator]; 7 | 8 | const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a); 9 | 10 | const reduce = curry((f, acc, iter) => { 11 | if (!iter) { 12 | iter = acc[Symbol.iterator](); 13 | acc = iter.next().value; 14 | } else { 15 | iter = iter[Symbol.iterator](); 16 | } 17 | return go1(acc, function recur(acc) { 18 | let cur; 19 | while (!(cur = iter.next()).done) { 20 | const a = cur.value; 21 | acc = f(acc, a); 22 | if (acc instanceof Promise) return acc.then(recur); 23 | } 24 | return acc; 25 | }); 26 | }); 27 | 28 | const go = (...args) => reduce((a, f) => f(a), args); 29 | 30 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 31 | 32 | const take = curry((l, iter) => { 33 | let res = []; 34 | iter = iter[Symbol.iterator](); 35 | let cur; 36 | while (!(cur = iter.next()).done) { 37 | const a = cur.value; 38 | res.push(a); 39 | if (res.length == l) return res; 40 | } 41 | return res; 42 | }); 43 | 44 | const takeAll = take(Infinity); 45 | 46 | const L = {}; 47 | 48 | L.range = function* (l) { 49 | let i = -1; 50 | while (++i < l) yield i; 51 | }; 52 | 53 | L.map = curry(function* (f, iter) { 54 | for (const a of iter) { 55 | yield f(a); 56 | } 57 | }); 58 | 59 | L.filter = curry(function* (f, iter) { 60 | for (const a of iter) { 61 | if (f(a)) yield a; 62 | } 63 | }); 64 | 65 | L.entries = function* (obj) { 66 | for (const k in obj) yield [k, obj[k]]; 67 | }; 68 | 69 | L.flatten = function* (iter) { 70 | for (const a of iter) { 71 | if (isIterable(a)) yield* a; 72 | else yield a; 73 | } 74 | }; 75 | 76 | L.deepFlat = function* f(iter) { 77 | for (const a of iter) { 78 | if (isIterable(a)) yield* f(a); 79 | else yield a; 80 | } 81 | }; 82 | 83 | L.flatMap = curry(pipe(L.map, L.flatten)); 84 | 85 | const map = curry(pipe(L.map, takeAll)); 86 | 87 | const filter = curry(pipe(L.filter, takeAll)); 88 | 89 | const find = curry((f, iter) => go( 90 | iter, 91 | L.filter(f), 92 | take(1), 93 | ([a]) => a)); 94 | 95 | const flatten = pipe(L.flatten, takeAll); 96 | 97 | const flatMap = curry(pipe(L.map, flatten)); 98 | 99 | var add = (a, b) => a + b; 100 | 101 | const range = l => { 102 | let i = -1; 103 | let res = []; 104 | while (++i < l) { 105 | res.push(i); 106 | } 107 | return res; 108 | }; 109 | -------------------------------------------------------------------------------- /10. 비동기 동시성 프로그래밍 2/1/html/1-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 지연 평가 + Promise - L.map, map, take 4 | 5 | 39 | 40 | ## Kleisli Composition - L.filter, filter, nop, take 41 | 42 | 59 | 60 | ## reduce에서 nop 지원 61 | 62 | 69 | 70 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /10. 비동기 동시성 프로그래밍 2/1/html/1-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take 4 | 5 | 40 | 41 | ## 즉시 병렬적으로 평가하기 - C.map, C.filter 42 | 43 | 47 | 48 | ## 즉시, 지연, Promise, 병렬적 조합하기 49 | 50 | -------------------------------------------------------------------------------- /10. 비동기 동시성 프로그래밍 2/1/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const isIterable = a => a && a[Symbol.iterator]; 7 | 8 | const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a); 9 | 10 | const reduceF = (acc, a, f) => 11 | a instanceof Promise ? 12 | a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) : 13 | f(acc, a); 14 | 15 | const head = iter => go1(take(1, iter), ([h]) => h); 16 | 17 | const reduce = curry((f, acc, iter) => { 18 | if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter); 19 | 20 | iter = iter[Symbol.iterator](); 21 | return go1(acc, function recur(acc) { 22 | let cur; 23 | while (!(cur = iter.next()).done) { 24 | acc = reduceF(acc, cur.value, f); 25 | if (acc instanceof Promise) return acc.then(recur); 26 | } 27 | return acc; 28 | }); 29 | }); 30 | 31 | const go = (...args) => reduce((a, f) => f(a), args); 32 | 33 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 34 | 35 | const take = curry((l, iter) => { 36 | let res = []; 37 | iter = iter[Symbol.iterator](); 38 | return function recur() { 39 | let cur; 40 | while (!(cur = iter.next()).done) { 41 | const a = cur.value; 42 | if (a instanceof Promise) { 43 | return a 44 | .then(a => (res.push(a), res).length == l ? res : recur()) 45 | .catch(e => e == nop ? recur() : Promise.reject(e)); 46 | } 47 | res.push(a); 48 | if (res.length == l) return res; 49 | } 50 | return res; 51 | }(); 52 | }); 53 | 54 | const takeAll = take(Infinity); 55 | 56 | const L = {}; 57 | 58 | L.range = function* (l) { 59 | let i = -1; 60 | while (++i < l) yield i; 61 | }; 62 | 63 | L.map = curry(function* (f, iter) { 64 | for (const a of iter) { 65 | yield go1(a, f); 66 | } 67 | }); 68 | 69 | const nop = Symbol('nop'); 70 | 71 | L.filter = curry(function* (f, iter) { 72 | for (const a of iter) { 73 | const b = go1(a, f); 74 | if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop)); 75 | else if (b) yield a; 76 | } 77 | }); 78 | 79 | L.entries = function* (obj) { 80 | for (const k in obj) yield [k, obj[k]]; 81 | }; 82 | 83 | L.flatten = function* (iter) { 84 | for (const a of iter) { 85 | if (isIterable(a)) yield* a; 86 | else yield a; 87 | } 88 | }; 89 | 90 | L.deepFlat = function* f(iter) { 91 | for (const a of iter) { 92 | if (isIterable(a)) yield* f(a); 93 | else yield a; 94 | } 95 | }; 96 | 97 | L.flatMap = curry(pipe(L.map, L.flatten)); 98 | 99 | const map = curry(pipe(L.map, takeAll)); 100 | 101 | const filter = curry(pipe(L.filter, takeAll)); 102 | 103 | const find = curry((f, iter) => go( 104 | iter, 105 | L.filter(f), 106 | take(1), 107 | ([a]) => a)); 108 | 109 | const flatten = pipe(L.flatten, takeAll); 110 | 111 | const flatMap = curry(pipe(L.map, flatten)); 112 | 113 | var add = (a, b) => a + b; 114 | 115 | const range = l => { 116 | let i = -1; 117 | let res = []; 118 | while (++i < l) { 119 | res.push(i); 120 | } 121 | return res; 122 | }; 123 | -------------------------------------------------------------------------------- /11. 비동기 동시성 프로그래밍 3/1/html/1-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## async/await 4 | 5 | 36 | 37 | ## QnA. Array.prototype.map이 있는데 왜 FxJS의 map 함수가 필요한지? 38 | 39 | 72 | 73 | ## QnA. 이제 비동기는 async/await로 제어할 수 있는데 왜 파이프라인이 필요한지? 74 | 75 | 106 | 107 | ## QnA. async/await와 파이프라인을 같이 사용하기도 하나요? 108 | 109 | 130 | 131 | ## QnA. 동기 상황에서 에러 핸들링은 어떻게 해야하는지? 132 | 133 | 147 | 148 | ## QnA. 비동기 상황에서 에러 핸들링은 어떻게 해야하는지? 149 | 150 | 168 | 169 | ## QnA. 동기/비동기 에러 핸들링에서의 파이프라인의 이점은? 170 | 171 | 190 | -------------------------------------------------------------------------------- /11. 비동기 동시성 프로그래밍 3/1/lib/fx.js: -------------------------------------------------------------------------------- 1 | const log = console.log; 2 | 3 | const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | const isIterable = a => a && a[Symbol.iterator]; 7 | 8 | const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a); 9 | 10 | const reduceF = (acc, a, f) => 11 | a instanceof Promise ? 12 | a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) : 13 | f(acc, a); 14 | 15 | const head = iter => go1(take(1, iter), ([h]) => h); 16 | 17 | const reduce = curry((f, acc, iter) => { 18 | if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter); 19 | 20 | iter = iter[Symbol.iterator](); 21 | return go1(acc, function recur(acc) { 22 | let cur; 23 | while (!(cur = iter.next()).done) { 24 | acc = reduceF(acc, cur.value, f); 25 | if (acc instanceof Promise) return acc.then(recur); 26 | } 27 | return acc; 28 | }); 29 | }); 30 | 31 | const go = (...args) => reduce((a, f) => f(a), args); 32 | 33 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 34 | 35 | const take = curry((l, iter) => { 36 | let res = []; 37 | iter = iter[Symbol.iterator](); 38 | return function recur() { 39 | let cur; 40 | while (!(cur = iter.next()).done) { 41 | const a = cur.value; 42 | if (a instanceof Promise) { 43 | return a 44 | .then(a => (res.push(a), res).length == l ? res : recur()) 45 | .catch(e => e == nop ? recur() : Promise.reject(e)); 46 | } 47 | res.push(a); 48 | if (res.length == l) return res; 49 | } 50 | return res; 51 | }(); 52 | }); 53 | 54 | const takeAll = take(Infinity); 55 | 56 | const L = {}; 57 | 58 | L.range = function* (l) { 59 | let i = -1; 60 | while (++i < l) yield i; 61 | }; 62 | 63 | L.map = curry(function* (f, iter) { 64 | for (const a of iter) { 65 | yield go1(a, f); 66 | } 67 | }); 68 | 69 | const nop = Symbol('nop'); 70 | 71 | L.filter = curry(function* (f, iter) { 72 | for (const a of iter) { 73 | const b = go1(a, f); 74 | if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop)); 75 | else if (b) yield a; 76 | } 77 | }); 78 | 79 | L.entries = function* (obj) { 80 | for (const k in obj) yield [k, obj[k]]; 81 | }; 82 | 83 | L.flatten = function* (iter) { 84 | for (const a of iter) { 85 | if (isIterable(a)) yield* a; 86 | else yield a; 87 | } 88 | }; 89 | 90 | L.deepFlat = function* f(iter) { 91 | for (const a of iter) { 92 | if (isIterable(a)) yield* f(a); 93 | else yield a; 94 | } 95 | }; 96 | 97 | L.flatMap = curry(pipe(L.map, L.flatten)); 98 | 99 | const map = curry(pipe(L.map, takeAll)); 100 | 101 | const filter = curry(pipe(L.filter, takeAll)); 102 | 103 | const find = curry((f, iter) => go( 104 | iter, 105 | L.filter(f), 106 | take(1), 107 | ([a]) => a)); 108 | 109 | const flatten = pipe(L.flatten, takeAll); 110 | 111 | const flatMap = curry(pipe(L.map, flatten)); 112 | 113 | var add = (a, b) => a + b; 114 | 115 | const range = l => { 116 | let i = -1; 117 | let res = []; 118 | while (++i < l) { 119 | res.push(i); 120 | } 121 | return res; 122 | }; 123 | 124 | const C = {}; 125 | 126 | function noop() { 127 | } 128 | 129 | const catchNoop = ([...arr]) => 130 | (arr.forEach(a => a instanceof Promise ? a.catch(noop) : a), arr); 131 | 132 | C.reduce = curry((f, acc, iter) => iter ? 133 | reduce(f, acc, catchNoop(iter)) : 134 | reduce(f, catchNoop(acc))); 135 | 136 | C.take = curry((l, iter) => take(l, catchNoop(iter))); 137 | 138 | C.takeAll = C.take(Infinity); 139 | 140 | C.map = curry(pipe(L.map, C.takeAll)); 141 | 142 | C.filter = curry(pipe(L.filter, C.takeAll)); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 함수형 프로그래밍과 ES6+ 예제 코드 2 | 3 | ## 강의 목록 4 | 1. 함수형 자바스크립트 기본기 5 | 1. 평가와 일급 6 | 2. 일급 함수 7 | 3. 고차 함수 8 | 4. 부수효과와 순수 함수 9 | 2. ES6에서의 순회와 이터러블/이터레이터 프로토콜 10 | 1. 기존과 달라진 ES6에서의 리스트 순회 11 | 2. Array, Set, Map을 통해 알아보는 이터러블/이터레이터 프로토콜 12 | 3. 사용자 정의 이터러블 13 | 4. 전개 연산자 14 | 3. 제너레이터와 이터레이터 15 | 1. 제너레이터와 이터레이터 16 | 2. odds 17 | 3. for...of, 전개 연산자, 구조 분해, 나머지 연산자 18 | 4. map, filter, reduce 19 | 1. map 20 | 2. 이터러블 프로토콜을 따른 map의 다형성 1 21 | 3. 이터러블 프로토콜을 따른 map의 다형성 2 22 | 4. filter 23 | 5. reduce 24 | 6. reduce 2 25 | 7. map+filter+reduce 중첩 사용과 함수형 사고 26 | 5. 코드를 값으로 다루어 표현력 높이기 27 | 1. go 28 | 2. pipe 29 | 3. go를 사용하여 읽기 좋은 코드로 만들기 30 | 4. go+curry를 사용하여 더 읽기 좋은 코드로 만들기 31 | 5. 함수 조합으로 함수 만들기 32 | 6. 장바구니 예제 33 | 1. 총 수량, 총 가격 34 | 2. HTML로 출력하기 35 | 7. 지연성 1 36 | 1. range와 느긋한 L.range 37 | 2. range와 느긋한 L.range 테스트 38 | 3. take 39 | 4. 제너레이터/이터레이터 프로토콜로 구현하는 지연 평가 40 | 5. L.map 41 | 6. L. filter 42 | 7. range, map, filter, take, reduce 중첩 사용 43 | 8. L.range, L.map, L.filter, take 의 평가 순서 44 | 9. 엄격한 계산과 느긋한 계산의 효율성 비교 45 | 10. map, filter 계열 함수들이 가지는 결합 법칙 46 | 11. ES6의 기본 규악을 통해 구현하는 지연 평가의 장점 47 | 8. 지연성 2 48 | 1. 결과를 만드는 함수 reduce, take 49 | 2. queryStr 함수 만들기 50 | 3. Array.prototype.join 보다 다형성이 높은 join 함수 51 | 4. take, find 52 | 5. L.map, L.filter로 map과 filter 만들기 53 | 6. L.flatten, flatten 54 | 7. L.flatMap, flatMap 55 | 8. 2차원 배열 다루기 56 | 9. 지연성 / 이터러블 중심 프로그래밍 실무적인 코드 57 | 9. 비동기/동시성 프로그래밍 1 58 | 1. callback과 Promise 59 | 2. 비동기를 값으로 만드는 Promise 60 | 3. 값으로서의 Promise 활용 61 | 4. 합성 관점에서의 Promise와 모나드 62 | 5. Kleisli Composition 관점에서의 Promise 63 | 6. go, pipe, reduce에서 비동기 제어 64 | 7. promise.then의 중요한 규칙 65 | 10. 비동기/동시성 프로그래밍 2 66 | 1. 지연 평가 + Promise - L.map, map, take 67 | 2. Kleisli Composition - L.filter, filter, nop, take 68 | 3. reduce에서 nop 지원 69 | 4. 지연 평가 + Promise의 효율성 70 | 5. 지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take 1 71 | 6. 지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take 2 72 | 7. 즉시 병렬적으로 평가하기 - C.map, C.filter 73 | 8. 즉시, 지연, Promise, 병렬적 조합하기 74 | 9. 코드 간단히 정리 75 | 10. Node.js에서 SQL 병렬 평가로 얻은 효율 76 | 11. 비동기/동시성 프로그래밍 3 77 | 1. async/await 78 | 2. Q&A) Array.prototype.map이 있는데 왜 FxJS의 map 함수가 필요한지? 79 | 3. Q&A) 이제 비동기는 async/await로 제어할 수 있는데 왜 파이프라인이 필요한지? 80 | 4. Q&A) async/await와 파이프라인을 같이 사용하기도 하는지? 81 | 5. Q&A) 동기 상황에서 에러 핸들링은 어떻게 해야하는지? 82 | 6. Q&A) 비동기 상황에서 에러 핸들링은 어떻게 해야하는지? 83 | 7. Q&A) 동기/비동기 에러 핸들링에서의 파이프라인의 이점은? 84 | -------------------------------------------------------------------------------- /fx.js: -------------------------------------------------------------------------------- 1 | export const log = console.log; 2 | 3 | export const curry = f => 4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._); 5 | 6 | export const isIterable = a => a && a[Symbol.iterator]; 7 | 8 | export const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a); 9 | 10 | const reduceF = (acc, a, f) => 11 | a instanceof Promise ? 12 | a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) : 13 | f(acc, a); 14 | 15 | export const head = iter => go1(take(1, iter), ([h]) => h); 16 | 17 | export const reduce = curry((f, acc, iter) => { 18 | if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter); 19 | 20 | iter = iter[Symbol.iterator](); 21 | return go1(acc, function recur(acc) { 22 | let cur; 23 | while (!(cur = iter.next()).done) { 24 | acc = reduceF(acc, cur.value, f); 25 | if (acc instanceof Promise) return acc.then(recur); 26 | } 27 | return acc; 28 | }); 29 | }); 30 | 31 | export const go = (...args) => reduce((a, f) => f(a), args); 32 | 33 | export const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 34 | 35 | export const take = curry((l, iter) => { 36 | let res = []; 37 | iter = iter[Symbol.iterator](); 38 | return function recur() { 39 | let cur; 40 | while (!(cur = iter.next()).done) { 41 | const a = cur.value; 42 | if (a instanceof Promise) { 43 | return a 44 | .then(a => (res.push(a), res).length == l ? res : recur()) 45 | .catch(e => e == nop ? recur() : Promise.reject(e)); 46 | } 47 | res.push(a); 48 | if (res.length == l) return res; 49 | } 50 | return res; 51 | }(); 52 | }); 53 | 54 | export const takeAll = take(Infinity); 55 | 56 | export const L = {}; 57 | 58 | L.range = function* (l) { 59 | let i = -1; 60 | while (++i < l) yield i; 61 | }; 62 | 63 | L.map = curry(function* (f, iter) { 64 | for (const a of iter) { 65 | yield go1(a, f); 66 | } 67 | }); 68 | 69 | export const nop = Symbol('nop'); 70 | 71 | L.filter = curry(function* (f, iter) { 72 | for (const a of iter) { 73 | const b = go1(a, f); 74 | if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop)); 75 | else if (b) yield a; 76 | } 77 | }); 78 | 79 | L.entries = function* (obj) { 80 | for (const k in obj) yield [k, obj[k]]; 81 | }; 82 | 83 | L.flatten = function* (iter) { 84 | for (const a of iter) { 85 | if (isIterable(a) && typeof a != 'string') yield* a; 86 | else yield a; 87 | } 88 | }; 89 | 90 | L.deepFlat = function* f(iter) { 91 | for (const a of iter) { 92 | if (isIterable(a) && typeof a != 'string') yield* f(a); 93 | else yield a; 94 | } 95 | }; 96 | 97 | L.flatMap = curry(pipe(L.map, L.flatten)); 98 | 99 | export const map = curry(pipe(L.map, takeAll)); 100 | 101 | export const filter = curry(pipe(L.filter, takeAll)); 102 | 103 | export const find = curry((f, iter) => go( 104 | iter, 105 | L.filter(f), 106 | take(1), 107 | ([a]) => a)); 108 | 109 | export const flatten = pipe(L.flatten, takeAll); 110 | 111 | export const flatMap = curry(pipe(L.map, flatten)); 112 | 113 | export const add = (a, b) => a + b; 114 | 115 | export const range = l => { 116 | let i = -1; 117 | let res = []; 118 | while (++i < l) { 119 | res.push(i); 120 | } 121 | return res; 122 | }; 123 | 124 | export const C = {}; 125 | 126 | export function noop() { 127 | } 128 | 129 | const catchNoop = ([...arr]) => 130 | (arr.forEach(a => a instanceof Promise ? a.catch(noop) : a), arr); 131 | 132 | C.reduce = curry((f, acc, iter) => iter ? 133 | reduce(f, acc, catchNoop(iter)) : 134 | reduce(f, catchNoop(acc))); 135 | 136 | C.take = curry((l, iter) => take(l, catchNoop(iter))); 137 | 138 | C.takeAll = C.take(Infinity); 139 | 140 | C.map = curry(pipe(L.map, C.takeAll)); 141 | 142 | C.filter = curry(pipe(L.filter, C.takeAll)); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('esm')(module)('./fx.js'); -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | 3 | Object.assign(global, require('../index.js')); 4 | 5 | describe('take', function () { 6 | it('take(1, [1, 2, 3])', () => { 7 | expect(take(1, [1, 2, 3])).to.eql([1]); 8 | }); 9 | it('take(2, [1, 2, 3])', () => { 10 | expect(take(2, [1, 2, 3])).to.eql([1, 2]); 11 | }); 12 | 13 | it('take(2, L.map(a => a + 1, [1, 2, 3]))', () => { 14 | expect(take(2, L.map(a => a + 1, [1, 2, 3]))).to.eql([2, 3]); 15 | }); 16 | 17 | it('take(2, L.filter(a => a % 2, [1, 2, 3, 4, 5]))', () => { 18 | expect(take(2, L.filter(a => a % 2, [1, 2, 3, 4, 5]))).to.eql([1, 3]); 19 | }); 20 | 21 | it('take(2, L.map(a => Promise.resolve(a + 1), [1, 2, 3]))', () => { 22 | go1( 23 | take(2, L.map(a => Promise.resolve(a + 1), [1, 2, 3])), 24 | _ => expect(_).to.eql([2, 3]) 25 | ); 26 | }); 27 | 28 | it('take(2, L.filter(a => Promise.resolve(a % 2), [1, 2, 3, 4, 5]))', () => { 29 | go1( 30 | take(2, L.filter(a => Promise.resolve(a % 2), [1, 2, 3])), 31 | _ => expect(_).to.eql([1, 3]) 32 | ); 33 | }); 34 | }); 35 | 36 | describe('reduce', function () { 37 | const add = (a, b) => a + b; 38 | it('reduce(add, [1, 2, 3])', () => { 39 | expect(reduce(add, [1, 2, 3])).to.eql(6); 40 | }); 41 | it('reduce(add, 10, [1, 2, 3])', () => { 42 | expect(reduce(add, 10, [1, 2, 3])).to.eql(16); 43 | }); 44 | it('reduce(add, [Promise.resolve(1), 2, 3])', () => { 45 | go1(reduce(add, [Promise.resolve(1), 2, 3]), _ => expect(_).to.eql(6)); 46 | }); 47 | it('reduce(add, L.filter(a => a % 2, [1, 2, 3, 4]))', () => { 48 | expect(reduce(add, L.filter(a => a % 2, [1, 2, 3, 4]))).to.eql(4); 49 | }); 50 | it('reduce(add, L.filter(a => Promise.resolve(a % 2), [1, 2, 3, 4]))', () => { 51 | go1(reduce(add, L.filter(a => Promise.resolve(a % 2), [1, 2, 3, 4])), _ => expect(_).to.eql(4)); 52 | }); 53 | it('reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => a + 10, [1, 2, 3, 4])))', () => { 54 | go1(reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => a + 10, [1, 2, 3, 4]))), _ => expect(_).to.eql(24)); 55 | }); 56 | it('reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => Promise.resolve(a + 10), [1, 2, 3, 4])))', () => { 57 | go1(reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => Promise.resolve(a + 10), [1, 2, 3, 4]))), _ => expect(_).to.eql(24)); 58 | }); 59 | }); 60 | 61 | describe('C.take', function () { 62 | function delay(val, time = 1000) { 63 | return new Promise(resolve => setTimeout(_ => resolve(val), time)); 64 | } 65 | 66 | it('C.take(2, L.map(a => delay(a, a * 100), [1, 2, 3]))', () => { 67 | go1( 68 | C.take(2, L.map(a => delay(a, 100), [1, 2, 3])), 69 | _ => expect(_).to.eql([1, 2]) 70 | ); 71 | }); 72 | 73 | it('C.take(2, L.filter(a => delay(a % 2, a * 100), [1, 2, 3]))', () => { 74 | go1( 75 | C.take(2, L.filter(a => delay(a % 2, 100), [1, 2, 3])), 76 | _ => expect(_).to.eql([1, 3]) 77 | ); 78 | }); 79 | }); 80 | 81 | describe('map', function () { 82 | return; 83 | function delay(val, time = 1000) { 84 | return new Promise(resolve => setTimeout(_ => resolve(val), time)); 85 | } 86 | 87 | it('map(a => delay(a+10, 1000), [1, 2, 3])', async function() { 88 | expect(await map(a => a + 10, [Promise.resolve(1), 2, 3])).to.eql([11, 12, 13]); 89 | }); 90 | 91 | it('map(a => delay(a+10, 1000), [1, 2, 3])', async function() { 92 | this.timeout(5000); 93 | expect(await map(a => delay(a+10, 1000), [1, 2, 3])).to.eql([11, 12, 13]); 94 | }); 95 | 96 | it('C.map(a => delay(a+10, 1000), [1, 2, 3])', async function() { 97 | this.timeout(1500); 98 | expect(await C.map(a => delay(a+10, 1000), [1, 2, 3])).to.eql([11, 12, 13]); 99 | }); 100 | }); 101 | 102 | describe('find', function () { 103 | it('find(a => a > 1, [1, 2, 3])', function() { 104 | expect(find(a => a > 1, [1, 2, 3])).to.eql(2); 105 | }); 106 | }); 107 | 108 | describe('go', function () { 109 | it(` 110 | go( 111 | 0, 112 | a => a + 1, 113 | a => a + 10, 114 | a => a + 100)`, function () { 115 | expect( 116 | go( 117 | 0, 118 | a => a + 1, 119 | a => a + 10, 120 | a => a + 100)).to.eql(111) 121 | }); 122 | 123 | it(` 124 | go( 125 | 0, 126 | a => { throw { hi: 'ho' } }, 127 | a => a + 10, 128 | a => a + 100)`, function () { 129 | try { 130 | go( 131 | 0, 132 | a => { throw { hi: 'ho' } }, 133 | a => a + 10, 134 | a => a + 100) 135 | } catch (a) { 136 | expect(a).to.eql({ hi: 'ho' }) 137 | } 138 | }); 139 | 140 | it(` 141 | go( 142 | 0, 143 | a => Promise.resolve(a + 1), 144 | a => a + 10, 145 | a => a + 100)`, async function () { 146 | expect( 147 | await go( 148 | 0, 149 | a => Promise.resolve(a + 1), 150 | a => a + 10, 151 | a => a + 100)).to.eql(111) 152 | }); 153 | 154 | it(` 155 | go( 156 | 0, 157 | a => Promise.resolve(a + 1), 158 | a => Promise.reject({ hi: 'ho' }), 159 | a => a + 100)`, async function () { 160 | try { 161 | await go( 162 | 0, 163 | a => Promise.resolve(a + 1), 164 | a => Promise.reject({ hi: 'ho' }), 165 | a => a + 100) 166 | } catch (a) { 167 | expect(a).to.eql({ hi: 'ho' }) 168 | } 169 | }); 170 | 171 | it(` 172 | go( 173 | 0, 174 | a => Promise.resolve(a + 1), 175 | a => { throw { hi: 'ho' } }, 176 | a => a + 100)`, async function () { 177 | try { 178 | await go( 179 | 0, 180 | a => Promise.resolve(a + 1), 181 | a => { throw { hi: 'ho' } }, 182 | a => a + 100) 183 | } catch (a) { 184 | expect(a).to.eql({ hi: 'ho' }) 185 | } 186 | }); 187 | }); --------------------------------------------------------------------------------