├── .gitignore
├── LICENSE
├── README.md
├── README_EN.md
├── abc.js
├── docs
├── Promise 와 abcjs의 비동기 프로그래밍 비교.html
├── Promise 와 abcjs의 비동기 프로그래밍 비교.md
├── Underscorejs 만들기 1 (_.map, _.each).html
├── Underscorejs 만들기 1 (_.map, _.each).md
├── 함수형 자바스크립트의 실용성 1.html
├── 함수형 자바스크립트의 실용성 1.md
├── 함수형 자바스크립트의 실용성 2.html
└── 함수형 자바스크립트의 실용성 2.md
├── etc
└── docs
│ ├── promise_and_abc.html
│ └── promise_and_abc.md
├── example
├── 01. A.html
├── 02. B.html
├── 03. C.html
├── 04. Pipeline with ABC.html
├── 05. Async (callback).html
├── 06. Async-2 (Promise).html
├── 07. each...map...find....html
├── 08. HTML Template.html
├── 08.1. HTML Template(TAB_SIZE 4).html
├── 09. IF ELSEIF ELSE.html
├── 10. B.all B.spread.html
├── 11. this.html
├── 13. CATCH.html
├── 14. Test.html
├── 15. ETC.html
├── 16. Arrow functions.html
├── 17. SEL.html
├── 18. Noti.html
├── box
│ ├── 1. Server Redering.html
│ └── 2. Create Post.html
└── js
│ ├── abc.box.js
│ ├── jquery.js
│ └── underscore.js
└── idea
├── box_noti_160926.html
└── box_noti_160926_2.html
/.gitignore:
--------------------------------------------------------------------------------
1 | /example/TDD.html
2 | .idea/
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 marpple
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 프로젝트 이전
2 | abcjs는 Partial.js로 변경되었습니다.
3 |
4 | https://github.com/marpple/partial.js
5 |
6 |
7 | # abcjs
8 | _abcjs는 함수형 자바스크립트(functional javascript) 라이브러리입니다._
9 |
10 | ## 특징
11 | - Web browser와 NodeJS에서 사용할 수 있습니다.
12 | - Promise, jQuery Deferred Object, Future 등의 모나드식 해법 보다 간결하고 편리한 비동기 제어를 지원합니다.
13 | - 비동기 함수일지라도 동기 함수를 작성하듯이 논리 구조를 만들 수 있습니다.
14 | - 비동기 제어 기능을 더한 `each`, `map`, `reduce`, `filter`, `reject`, `find`, `findIndex`, `some`, `every`, `uniq` 함수가 있습니다.
15 | - Jade와 비슷한 HTML Template 함수가 있습니다. 함수 사용과 비동기 함수를 지원합니다.
16 | - sql 등을 작성하기 편한 함수가 있습니다.
17 | - 다른 자바스크립트 라이브러리에 대한 의존성이 없는 작은 라이브러리입니다.
18 | - _Respect Underscorejs!_
19 |
20 | ## 함수형 자바스크립트 관련글
21 | 1. [함수형 자바스크립트의 실용성 1](https://github.com/marpple/abc-functional-javascript/wiki/%ED%95%A8%EC%88%98%ED%98%95-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%8B%A4%EC%9A%A9%EC%84%B1-1)
22 | 2. [함수형 자바스크립트의 실용성 2](https://github.com/marpple/abc-functional-javascript/wiki/%ED%95%A8%EC%88%98%ED%98%95-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%8B%A4%EC%9A%A9%EC%84%B1-2)
23 | 3. [Underscorejs 만들기 1](https://github.com/marpple/abc-functional-javascript/wiki/Underscorejs-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-(_.map,-_.each))
24 | 4. [Promise와 abcjs의 비동기 프로그래밍 비교](https://github.com/marpple/abc-functional-javascript/wiki/Promise%EC%99%80-abcjs%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EB%B9%84%EA%B5%90)
25 |
26 | ## 시작하기
27 | __abcjs와 함께 함수형 프로그래밍을 즐겨보세요.__
28 |
29 | ## 목차
30 | - [A (apply)](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#01-a)
31 | - [B (bind)](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#02-b)
32 | - [C (call)](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#03-c)
33 | - [Pipeline with ABC](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#04-pipeline-with-abc)
34 | - [Async (callback)](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#05-asynccallback)
35 | - [Async-2 (Promise)](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#06-async-2promise)
36 | - [each, map, find, ...](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#07-eachmapfind)
37 | - [HTML Template](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#08-html-template)
38 | - [IF ELSEIF ELSE](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#09-if-elseif-else)
39 | - [B.all B.spread](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#10-ball-bspread)
40 | - [this](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#11-this)
41 | - [ETC](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#12-etc)
42 | - [throw, ERR, CATCH](https://github.com/marpple/abc-functional-javascript/blob/master/README.md#13-throw-err-catch)
43 |
44 | ### 01. [A](https://github.com/marpple/abc-functional-javascript/blob/master/README.md/blob/master/example/01.%20A.html)
45 |
46 | `A`는 `apply`와 비슷합니다.
47 |
48 | ```javascript
49 | function add(a, b) {
50 | return a + b;
51 | }
52 |
53 | var r1 = A([20, 2], add); // add.apply(undefined, [20, 2]);
54 | console.log(r1);
55 | // 22
56 | ```
57 |
58 |
59 |
60 | 왼쪽에서 오른쪽, 위에서 아래로 읽는게 편안합니다. 그래서 `A`는 인자와 함수사용에 대한 방향을 바꾸었습니다.
61 | ```javascript
62 | function minus(a, b) {
63 | return a - b;
64 | }
65 |
66 | var r2 = A([20, 5], function() {
67 | return A(arguments, minus);
68 | });
69 | console.log(r2);
70 | // 15
71 | ```
72 | 배열이나 arguments객체를 사용하면 됩니다.
73 |
74 |
75 | ### 02. [B](https://github.com/marpple/abc-functional-javascript/blob/master/example/02.%20B.html)
76 | `B`는 `this`를 제외한 `bind`라고 생각하면 쉽습니다.
77 | 혹은 underscore의 `_.partial`과 유사합니다.
78 | ```javascript
79 | function minus(a, b) {
80 | return a - b;
81 | }
82 |
83 | var f1 = B(10, minus);
84 | var r1 = f1(20);
85 | console.log(r1);
86 | // -10
87 | ```
88 |
89 |
90 | `B`를 `X`와 함께 사용해보세요.
91 | `X`를 통해 이후 실행시에 받게될 인자의 자리를 지정해둘 수 있습니다.
92 | ```javascript
93 | var f2 = B(X, 10, minus);
94 | var r2 = f2(20);
95 | console.log(r2);
96 | // 10
97 |
98 | function minus2(a, b, c, d) {
99 | return a - b - c - d;
100 | }
101 |
102 | var f3 = B(4, X, X, 1, minus2);
103 | var r3 = f3(3, 2);
104 | console.log(r3);
105 | // -2
106 | ```
107 |
108 |
109 |
110 | ### 03. [C](https://github.com/marpple/abc-functional-javascript/blob/master/example/03.%20C.html)
111 | `C`는 `this`를 제외한 `call`이라고 생각하면 쉽습니다.
112 | ```javascript
113 | function minus(a, b) {
114 | return a - b;
115 | }
116 |
117 | var r1 = C(10, 20, minus);
118 | console.log(r1);
119 | // -10
120 |
121 | var r2 = C(20, 10, minus);
122 | console.log(r2);
123 | // 10
124 | ```
125 |
126 |
127 |
128 | ### 04. [Pipeline with ABC](https://github.com/marpple/abc-functional-javascript/blob/master/example/04.%20Pipeline%20with%20ABC.html)
129 | abcjs는 underscore의 `_.compose`나 jQuery의 chain과 유사한 코드 패턴을 지원합니다.
130 | 객체지향적인 체인방식은 연속적으로 값을 변경해나갈 수 있지만 자신이 가진 값을 바꾸는 방법이기 때문에 사용은 쉽지만 구현이 어렵습니다.
131 | 또한 자신이 가진 메소드와 자신의 값만을 사용하기 때문에 제약이 있습니다. 파이프라인과 같은 연속적인 함수 실행 방식은 체인방식보다 유연하고 순수 함수들을 만들 수 있어 좋습니다.
132 |
133 | abcjs에서는 별도의 파이프라인 함수를 만들지 않고 기본 함수 실행 함수인 ABC에서 파이프라인 패턴을 바로 사용할수 있도록 했습니다.
134 | 또한 `_.compose`와 달리 읽기 쉬운 방향으로 순서를 바꿨습니다. 쉽습니다. 마지막 인자 자리에 함수 대신 배열로 함수들을 나열하기만 하면 됩니다.
135 | ```javascript
136 | function sum(a, b) {
137 | return a + b;
138 | }
139 |
140 | function square(a) {
141 | return a * a;
142 | }
143 |
144 | var r1 = C(1, 2, [
145 | sum,
146 | square,
147 | square,
148 | function(a) {
149 | console.log(a); // 81
150 | return a - 20;
151 | }]);
152 |
153 | console.log(r1);
154 | // 61
155 | ```
156 |
157 |
158 | `1, 2` 인자는 `sum`에게 `sum`의 결과는 `square`에게 그리고 그 함수의 결과는 계속 다음 함수의 인자로 넘어갑니다. 그리고 마지막 함수의 return 값은 `C`의 실행 결과가 됩니다.
159 |
160 | `A`를 이용하면 배열과 arguments객체로 파이프라인 패턴을 사용할 수 있습니다.
161 | ```javascript
162 | A([1, 2], [
163 | sum,
164 | square,
165 | square,
166 | function(a) {
167 | return a - 20;
168 | },
169 | function(r1) {
170 | console.log(r1);
171 | // 61
172 | }]);
173 | ```
174 |
175 |
176 | `B` 함수를 이용하면 작은 함수를 모아 큰 함수를 만들 수 있습니다. 혹은 큰 함수를 작은 단위로 쪼갤 수 있습니다. 작은 함수가 많아지면 코드 재활용률을 높일 수 있습니다.
177 | ```javascript
178 | var f1 = B([
179 | sum,
180 | square,
181 | square,
182 | function(a) {
183 | console.log(a); // 81
184 | return a - 20;
185 | }]);
186 |
187 | var r3 = f1(1, 2);
188 | console.log(r3);
189 | // 61
190 | ```
191 |
192 |
193 | 함수 조합에서도 `X`와 함께 사용하여 인자를 미리 적용 해둘 수 있습니다.
194 | ```javascript
195 | function minus(a, b) {
196 | return a - b;
197 | }
198 |
199 | var f2 = B(X, 10, [
200 | f1,
201 | B(X, 11, minus)
202 | ]);
203 |
204 | var r4 = f2(-7);
205 | console.log(r4);
206 | // 50
207 | ```
208 |
209 |
210 | chain 패턴이나 `_.compose`, `Promise` 등의 일종의 파이프라인 혹은 모나드 등에서 아쉬운점은 함수 모음의 첫번째 함수를 제외하고는 인자를 하나만 받을 수 있다는 점 입니다.
211 |
212 | `B(X, 11, minus)` 를 통해 두개의 인자가 사용되도록 했지만 여전히 사실은 위에서 부터 내려오는 인자는 하나입니다.
213 |
214 | 인자를 하나만 받는 함수만 조립할 수 있다면 실용성이 떨어지고 인자가 두개 이상 필요한 함수를 사용하기 위해선 항상 wrapper 함수가 있어야합니다.
215 | 이를 위해 `MR`이 있습니다. `MR`을 이용하면 다음 함수가 여러개의 결과를 인자로 받을 수 있습니다.
216 | `MR` 사용은 아래와 같은 두가지 사용법이 있습니다.
217 | ```javascript
218 | C(3, 2, [
219 | function(a, b) {
220 | return MR(a + b, a - b, a * b); // multiple results
221 | },
222 | function(a, b, c) {
223 | console.log(a, b, c);
224 | return MR(a, c); // multiple results
225 | },
226 | function(a, c) {
227 | console.log(a, c);
228 | return arguments;
229 | },
230 | C.toMR, // arguments to multiple results
231 | function(a, c) {
232 | console.log(a, c); // 5, 6
233 | }]);
234 | ```
235 |
236 |
237 | `B`를 통해 함수를 정의하면 ABC를 이용하지 않고도 `MR()`을 동작 시킬 수 있습니다.
238 | ```javascript
239 | var minus = B(function(a, b) {
240 | console.log(a, b); // 20, 10
241 | return a - b;
242 | });
243 |
244 | var r5 = minus(10, 20);
245 | console.log(r5); // -10
246 |
247 | /* swap 함수의 multiple results를 바로 minus에게 넘김 */
248 | var swap = function(a, b) {
249 | return MR(b, a);
250 | };
251 | var r6 = minus(swap(10, 20));
252 | console.log(r6);
253 | // 10
254 | ```
255 | `MR`은 Go언어의 Multiple Results와 비슷합니다.
256 | [GO Lang - Multiple Results](https://tour.golang.org/basics/6)
257 |
258 |
259 | 함수 조립의 즐거움을 맛보세요.
260 | ```javascript
261 | var difference = B([
262 | function(a, b) {
263 | return MR(Math.max(a, b), Math.min(a, b));
264 | },
265 | minus
266 | ]);
267 |
268 | var r7 = difference(10, 20);
269 | console.log(r7);
270 | // 10
271 |
272 | var r8 = difference(30, 10);
273 | console.log(r8);
274 | // 20
275 | ```
276 |
277 |
278 |
279 | abcjs의 다른 함수를 활용하면 `difference`를 아래와 같이 구현할 수도 있습니다.
280 | ```javascript
281 | var difference2 = B([
282 | C.args, // function() { return arguments },
283 | _.toArray,
284 | B.M('sort'), // function(a) { return a.sort(); },
285 | B.M('reverse'), // function(a) { return a.reverse(); },
286 | C.toMR, // array to multiple results
287 | minus]);
288 |
289 | var r9 = difference2(10, 20);
290 | console.log(r9);
291 | // 10
292 |
293 | var r10 = difference2(30, 10);
294 | console.log(r10);
295 | // 20
296 |
297 | var difference3 = B([minus, Math.abs]);
298 |
299 | var r11 = difference3(10, 20);
300 | console.log(r11);
301 | // 10
302 |
303 | var r12 = difference3(30, 10);
304 | console.log(r12);
305 | // 20
306 | ```
307 |
308 |
309 |
310 | ### 05. [Async(callback)](https://github.com/marpple/abc-functional-javascript/blob/master/example/05.%20Async%20(callback).html)
311 | abcjs에서는 비동기 제어와 관련된 다양한 기능을 제공합니다.
312 | 파이프라인에서 사용할 콜백 패턴의 함수를 `CB` 함수로 한번 넘겨 두기만 하면 됩니다.
313 | `CB`가 감싸졌던 `f1`이라는 함수를 파이프라인에 넣어두면 `C`함수 안에서 `f1`에게 필요한 callback 함수를 생성하여 마지막 인자로 넣어줍니다.
314 | 생성된 callback 함수로 값을 꺼낸 후 파이프라인의 다음 함수에게 결과를 전달합니다.
315 | `CB`로 감싼 후 callback 함수 인자의 자리만 제외하고 실행하거나 `CB(익명함수)`를 통해 `C`에게 받은 callback 함수를 사용하여 결과를 다음 함수로 전달할 수 있습니다.
316 | Promise와 달리 여러개의 인자를 다음 함수로 주고 있던 함수이더라도 여러개의 인자로 받을 수 있습니다.
317 | 이런 콜백 함수와 사용할때 유용합니다. `function(err, data) { ... }`
318 |
319 | ```javascript
320 | function delay(func) {
321 | setTimeout(function() {
322 | func();
323 | }, 1000);
324 | }
325 |
326 | function sum(a, b, cb) {
327 | delay(function() {
328 | cb(a + b);
329 | });
330 | }
331 |
332 | function minus(a, b, cb) {
333 | delay(function() {
334 | cb(a - b);
335 | });
336 | }
337 |
338 | function square(a, cb) {
339 | delay(function() {
340 | cb(a * a);
341 | });
342 | }
343 |
344 | sum(10, 20, function (r) {
345 | console.log(r); // 30
346 | });
347 |
348 | minus(10, 7, function(r) {
349 | console.log(r); // 3
350 | });
351 |
352 | sum(5, 7, function(r) {
353 | minus(r, 5, function(r) {
354 | square(r, function(r) {
355 | console.log(r); // 49
356 | });
357 | });
358 | });
359 | ```
360 |
361 | `CB`를 감싸두기만 하면 됩니다. 파이프라인이 넘겨준 `CB` 함수를 이용하여 값을 넘기면 다음 함수로 전달됩니다.
362 | ```javascript
363 | CB(sum, minus, square);
364 |
365 | C(5, 10, [
366 | sum,
367 | CB(function(r, cb) {
368 | minus(r, 5, cb);
369 | }),
370 | square,
371 | function(r) {
372 | console.log(r); // 100
373 | }
374 | ]);
375 | ```
376 |
377 | `B`를 활용하여 좀더 간략하게 만들 수 있습니다.
378 | ```javascript
379 | C(5, 9, [
380 | sum,
381 | B(X, 5, minus),
382 | square,
383 | function(r) {
384 | console.log(r); // 81
385 | }
386 | ]);
387 | ```
388 |
389 |
390 | ```javascript
391 | .then(function() {
392 | return new Promise(function(rs) {
393 | sum(5, 10, rs)
394 | });
395 | }).then(function() {
396 | return new Promise(function(rs) {
397 | minus(5, 10, rs)
398 | });
399 | });
400 | ```
401 |
402 | 이 방법은 일반 콜백함수를 Promise로 제어하는 것보다 간단합니다. 또한 새로운 함수를 만들지 않으며 `CB`를 감싼 뒤라도 콜백 패턴의 함수를 원래 사용하던대로 사용할 수도 있습니다.
403 | 유사한 개념이지만 새로운 함수를 뱉는 `Promise.promisify` 보다 단순하고 새로운 개념을 알 필요가 없습니다.
404 | `.then()`과 달리 multiple results도 가능합니다.
405 | ```javascript
406 | C(5, 9, [
407 | sum,
408 | CB(function(a, cb) {
409 | delay(function() {
410 | cb(a, 10); // auto multiple results
411 | });
412 | }),
413 | minus,
414 | function(r) {
415 | console.log(r); // 4
416 | }
417 | ]);
418 | ```
419 |
420 |
421 | ```javascript
422 | var $ = {};
423 |
424 | $.get = function(url, cb) {
425 | delay(function() {
426 | cb({
427 | a: 5,
428 | b: 3
429 | });
430 | });
431 | };
432 |
433 | $.post = function(url, data, cb) {
434 | delay(function() {
435 | cb(_.extend(data, { created_at: new Date() }));
436 | });
437 | };
438 |
439 | $.put = function(url, data, cb) {
440 | delay(function() {
441 | cb(_.extend(data, { updated_at: new Date() }));
442 | });
443 | };
444 | ```
445 | `$.get`, `$.post` 등과 유사한 위와 같은 함수가 있다고 가정할때 아래와 같이 활용할 수 있습니다.
446 | ```javascript
447 | C([
448 | CB(function(cb) {
449 | $.get("/get_data", cb);
450 | },
451 | function(data, cb) {
452 | console.log(_.clone(data)); // {a: 5, b: 3}
453 | $.post("/post_data", _.extend(data, { c: 10 }), cb);
454 | },
455 | function(data, cb) {
456 | console.log(_.clone(data)); // {a: 5, b: 3, c: 10, created_at: Tue Sep 13 2016 04:01:19 GMT+0900 (KST)}
457 | $.put("/put_data", _.extend(data, { c: 5 }), cb);
458 | }),
459 | function(r) {
460 | console.log(r);
461 | // {a: 5, b: 3, c: 5, created_at: Tue Sep 13 2016 04:03:58 GMT+0900 (KST), updated_at: Tue Sep 13 2016 04:03:59 GMT+0900 (KST)}
462 | }
463 | ]);
464 | ```
465 |
466 | `B`를 활용하면 더욱 깔끔하게 만들 수 있습니다.
467 | ```javascript
468 | CB($.get, $.post, $.put);
469 |
470 | C([
471 | B("/get_data", $.get),
472 | B({ c: 20 }, _.extend),
473 | B("/post_data", $.post),
474 | B({ c: 30 }, _.extend),
475 | B("/put_data", $.put),
476 | function(r) {
477 | console.log(r);
478 | // {c: 20, a: 5, b: 3, created_at: Tue Sep 13 2016 04:01:19 GMT+0900 (KST), updated_at: Tue Sep 13 2016 04:01:20 GMT+0900 (KST)}
479 | }
480 | ]);
481 | ```
482 |
483 | `J`와 `MR`을 활용하면 다음과 같이 사용할 수 있습니다.
484 | ```javascript
485 | function J(v) {
486 | return function() {
487 | return v;
488 | }
489 | }
490 | ```
491 |
492 | ```javascript
493 | C([
494 | J(MR("/post_data", { aka: 'Cojamm' })),
495 | $.post,
496 | function(r) {
497 | console.log(r); // {aka: "Cojamm", created_at: Tue Sep 13 2016 04:01:18 GMT+0900 (KST)}
498 | }
499 | ]);
500 | ```
501 | _*참고 - 실제 jQuery의 `$.get` 함수등은 `{ then: func.. }` 를 리턴하기 때문에 `CB`로 감싸는 방식으로 구현할 필요 없습니다._
502 |
503 | ```javascript
504 | /* B를 감싸서 함수를 만들어두면 아래와 같이도 사용할 수 있습니다. 비동기 함수를 아래와 같이도 사용할 수 있습니다. */
505 | CB(sum, minus, square);
506 | function sum(a, b, cb) {
507 | delay(function() {
508 | cb(a + b);
509 | });
510 | }
511 | function minus(a, b, cb) {
512 | delay(function() {
513 | cb(a - b);
514 | });
515 | }
516 | function square(a, cb) {
517 | delay(function() {
518 | cb(a * a);
519 | });
520 | }
521 | var sq = B(square);
522 | var m5 = B(X, 5, minus);
523 | var s = B(sum);
524 | var log = B(function() {
525 | console.log.apply(console, arguments);
526 | });
527 | log(sq(m5(s(10, 10))));
528 | console.log('216 line');
529 | ```
530 |
531 | ### 06. [Async-2(Promise)](https://github.com/marpple/abc-functional-javascript/blob/master/example/06.%20Async-2%20(Promise).html)
532 | abcjs는 Promise가 필요 없지만 `.then`을 리턴하는 함수의 비동기 제어를 지원합니다.
533 | ```javascript
534 | function delay() {
535 | return new Promise(function(rs) {
536 | setTimeout(function() {
537 | rs();
538 | }, 1000);
539 | })
540 | }
541 |
542 | function sum(a, b) {
543 | return delay().then(function() {
544 | return a + b;
545 | });
546 | }
547 |
548 | function minus(a, b) {
549 | return delay().then(function() {
550 | return a - b;
551 | });
552 | }
553 |
554 | function square(a) {
555 | return delay().then(function() {
556 | return a * a;
557 | });
558 | }
559 |
560 | // Promise
561 | sum(2, 4)
562 | .then(function(r) {
563 | return minus(r, 4);
564 | }).then(function(r) {
565 | return square(r);
566 | }).then(function(r) {
567 | console.log(r); // 4
568 | });
569 |
570 | // ABC
571 | C(3, 6, [
572 | sum,
573 | function(r) {
574 | return minus(r, 5);
575 | },
576 | square,
577 | function(r) {
578 | console.log(r); // 16
579 | }]);
580 | ```
581 |
582 |
583 | 아래와 같이 마지막 결과를 `then`으로 받을 수 있어 Promise와 함께 사용이 가능합니다.
584 | ```javascript
585 | C(5, 5, [
586 | sum,
587 | square
588 | ]).then(function(r) {
589 | console.log(r); // 100
590 | });
591 |
592 | sum(2, 4)
593 | .then(function(r) {
594 | return C(r, 2, [
595 | sum,
596 | square
597 | ]);
598 | }).then(function(r) {
599 | console.log(r); // 64
600 | });
601 |
602 | // B를 사용하면 더욱 간단하게 만들 수 있습니다.
603 | sum(2, 4)
604 | .then(B(X, 2, [
605 | sum,
606 | square
607 | ])).then(function(r) {
608 | console.log(r); // 64
609 | });
610 |
611 | // CB와 함께 사용 가능합니다.
612 | C(5, 5, [
613 | sum,
614 | square,
615 | CB(function(r, cb) {
616 | cb(r / 2)
617 | })
618 | ]).then(function(r) {
619 | console.log(r); // 50
620 | });
621 | ```
622 | 만일 es6 Promise나 Promise Library가 있다면 `C()`는 Promise 객체를 리턴합니다.
623 | 그렇지 않다면 `C()`의 리턴 값이 `{}.then()`의 형태를 띄지만 Promise 객체는 아닙니다.
624 | `function has_promise() { (window || global).Promise.prototype.then; }`
625 | `has_promise() ? new Promise(function(rs) { resolve = rs; }) : { then: function(rs) { resolve = rs; } })`
626 |
627 |
628 |
629 | ### 07. [each...map...find...](https://github.com/marpple/abc-functional-javascript/blob/master/example/07.%20each...map...find....html)
630 | underscorejs의 `each`, `map`, `find` 등의 함수보다 편의성을 높였습니다.
631 | 1. this는 사용하지 않으며 대신 iteratee 함수에게 인자를 여러개를 넘길 수 있습니다.
632 | - ex) [1, 2, 3], arg1, arg2, arg3 => value, key, list, arg1, arg2, arg3 ...
633 | 2. 비동기 제어가 됩니다.
634 | 3. iteratee나 predicate 함수를 파이프라인으로 만들 수 있습니다.
635 | 4. 즉시 실행인 `C.map`과 부분 실행 컨셉의 `B.map`이 있어 파이프라인에서 편하게 사용할 수 있습니다.
636 |
637 | ```javascript
638 | function delay(func) {
639 | setTimeout(function() {
640 | func();
641 | }, 1000);
642 | }
643 |
644 | function sum(a, b) {
645 | return a + b;
646 | }
647 |
648 | function square(a) {
649 | return a * a;
650 | }
651 |
652 | C.each([1, 2, 3], function(v) {
653 | console.log(v);
654 | });
655 | // 1
656 | // 2
657 | // 3
658 |
659 | var r1 = C.map([1, 2, 3], square);
660 | console.log(r1); // [1, 4, 9]
661 |
662 | /* iteratee 함수를 파이프라인으로 만들 수 있습니다. */
663 | var r2 = C.map({ a: 1, b: 2, c: 3 }, [I, square]);
664 | console.log(r2); // [1, 4, 9]
665 |
666 | var r3 = C.map([1, 2, 3], 5, function(v, i, l, a) { //val, idx, list, 5
667 | return sum(v, a);
668 | });
669 | console.log(r3); // [6, 7, 8]
670 |
671 | /* B.args는 들어온 인자중 원하는 번째의 인자를 선택하여 Multiple Results로 만듭니다. */
672 | var r4 = C.map([1, 2, 3], 5, [B.args(0, 3), sum]);
673 | console.log(r4); // [6, 7, 8]
674 |
675 | var r6 =
676 | C({ a: 1, b: 2, c: 3 }, [
677 | B.map(I), // [1, 2, 3] // function I(v) { return v; }
678 | B.map(square), // [1, 4, 9]
679 | function(v) { return MR(v, 0); },
680 | B.reduce(function(memo, v) {
681 | return memo + v;
682 | })]);
683 | console.log(r6); // 14
684 |
685 | var minus = function(a, b) {
686 | return a - b;
687 | };
688 |
689 | C({ a: 1, b: 2, c: 3 }, [
690 | B.map(I), // [1, 2, 3]
691 | B.map(square), // [1, 4, 9]
692 | function(v) { return MR(v, 0); },
693 | B.reduce([B.args(0, 1), minus]), // 동기
694 | function(r7) {
695 | console.log(r7); // -14
696 | }]);
697 |
698 | var minus2 = CB(function(a, b, cb) {
699 | delay(function() {
700 | cb(a - b);
701 | });
702 | });
703 | ```
704 |
705 |
706 | 비동기 함수가 있어도 코딩 패턴을 바꾸지 않고 편하게 코딩할 수 있습니다.
707 | ```javascript
708 | C({ a: 1, b: 2, c: 3 }, [
709 | B.map(I), // [1, 2, 3]
710 | B.map(square), // [1, 4, 9]
711 | function(v) { return MR(v, 0); },
712 | B.reduce([B.args(0, 1), minus2]), // 비동기
713 | function(r7) {
714 | console.log(r7); // -14
715 | }]);
716 |
717 | var users = [
718 | { id: 1, age: 20, activated: true },
719 | { id: 2, age: 20, activated: false },
720 | { id: 3, age: 31, activated: false },
721 | { id: 4, age: 32, activated: true },
722 | { id: 5, age: 17, activated: true },
723 | { id: 6, age: 32, activated: true }
724 | ];
725 |
726 | C(users, [
727 | B.filter(B.v('activated')),
728 | B.reject(function(user) {
729 | return user.age > 30;
730 | }),
731 | B.map(B.v('age')),
732 | function(r8) {
733 | console.log(r8); // [20, 17]
734 | }]);
735 |
736 | C(users, [
737 | B.uniq('$.age'),
738 | B.map(B.v('id')),
739 | function(r9) {
740 | console.log(r9); // [1, 3, 4, 5]
741 | }]);
742 |
743 | C(users, [
744 | G[":reject :age > 30"] = B.reject(function(user) {
745 | return user.age > 30;
746 | }),
747 | B.every(B.v('activated')),
748 | function(r10) {
749 | console.log(r10); // false
750 | }]);
751 |
752 | C(users, [
753 | G[":reject :age > 30"],
754 | B.some(B.v('activated')),
755 | function(r11) {
756 | console.log(r11); // true
757 | }]);
758 | ```
759 |
760 | array, object, [object, object, object] 등을 편하게 다룰 수 있는 유용한 함수들입니다.
761 | `C.each`, `C.map`, `C.reduce`, `C.filter`, `C.reject`, `C.find`, `C.find_index`, `C.some`, `C.every`, `C.uniq`,
762 | `B.each`, `map`, `B.reduce`, `B.filter`, `B.reject`, `B.find`, `B.find_index`, `B.some`, `B.every`, `B.uniq`
763 |
764 |
765 |
766 | ### 08. [HTML Template](https://github.com/marpple/abc-functional-javascript/blob/master/example/08.%20HTML%20Template.html)
767 | abcjs에는 html을 효율적으로 만들 수 있는 template 함수인 `H`, `H.each`, 일반 문자열을 효율적으로 만들 수 있는 `S`, `S.each`가 있습니다.
768 | `H`, `H.each`, `S`, `S.each`는 모두 비동기 제어를 지원하고 abcjs의 다른 함수들과 함께 사용하기 좋습니다.
769 | ```javascript
770 | C([
771 | H('', '\
772 | .member[style="border: 1px solid #000; padding: 20px;"]\
773 | h3 People\
774 | ul\
775 | li Cojamm\
776 | li BJ\
777 | li JM\
778 | li PJ\
779 | li HA\
780 | li JE\
781 | #id1.class1.class2[class=class3] hi\
782 | .service\
783 | a[href=http://www.marpple.com target=_blank] http://www.marpple.com\
784 | textarea[rows=10].\
785 | Custom T-Shirts\
786 | \
787 | \
788 | Design Platform\
789 | Artwork\
790 | br\
791 | /* br\
792 | br*/\
793 | // p hi\
794 | p hello\
795 | // br\
796 | textarea\
797 | | foo bar\
798 | | hello world'),
799 | $,
800 | B.M('appendTo', 'body')]);
801 |
802 |
803 | hr();
804 | ```
805 | **참고 - 주석은 반드시 맨 앞줄에서 시작되어야합니다.**
806 |
807 |
808 | H-S의 특징
809 | 1. H-S는 js내에서 사용하기위해 만들어졌습니다. 짧게 작성할 수 있게 하기 위해 jade의 문법과 닮았습니다.
810 | 2. jade보다 더욱 css 문법과 동일합니다.
811 | 3. javascript 함수 사용이 편리합니다.
812 | 4. `""`,`''` 등을 생략할 수 있어 문자열을 다루는데 좀더 편리합니다.
813 | 5. handlebars 등의 helper 보다 더욱 편리하게 template을 위한 함수를 만들 수 있습니다.
814 |
815 |
816 | 데이터 치환
817 | ```javascript
818 | C({ name: 'Cojamm', age: 32 }, [
819 | H('user', '\
820 | .person\
821 | .name {{user.name}}\
822 | .age {{user.age}}'),
823 | $,
824 | B.M('appendTo', 'body')]);
825 |
826 | hr();
827 |
828 | var post = {
829 | body: '하이랩이나 잘하라고',
830 | name: 'Cojamm',
831 | created_at: new Date()
832 | };
833 |
834 | C(post, [
835 | H('post', '\
836 | .name 글쓴이: {{post.name}}\
837 | .body 내용: {{post.body}}\
838 | .created_at 시간: {{post.created_at}}'),
839 | $,
840 | B.M('appendTo', 'body')]);
841 |
842 | hr();
843 |
844 | delete post.name;
845 |
846 | window.moment_lll = B([moment, B.M('format', 'lll')]);
847 |
848 | C(post, [
849 | H('post', '\
850 | .name 글쓴이: {{post.name || "익명"}}\
851 | .body 내용: {{{post.body}}}\
852 | .created_at 시간: {{moment_lll(post.created_at)}}'),
853 | $,
854 | B.M('appendTo', 'body')]);
855 |
856 | hr();
857 | ```
858 |
859 |
860 | `H`의 인자를 여러개를 넘기면 template 문자열을 합치는데 그중 함수를 넣어 함수를 실행 시킬 수 있습니다.
861 | 복잡한 로직은 함수로 빼서 구현 할 수 있습니다.
862 | ```javascript
863 | var songs = [
864 | 'The Riddle Of The Model',
865 | 'Up',
866 | 'To Find You',
867 | 'A Beautiful Sea',
868 | 'Drive It Like You Stole It',
869 | 'Up (Bedroom Mix)',
870 | 'Girls',
871 | 'Brown Shoes'];
872 |
873 | C(songs, [
874 | H('songs', '\
875 | h3 Sing Street OST\
876 | ul\
877 | {{{C(songs, ', function(songs) {
878 | return _.map(songs, function(song, i) {
879 | return '
' + (i+1) + '. ' + song + '
';
880 | }).join("");
881 | },')}}}'),
882 | $,
883 | B.M('appendTo', 'body')]);
884 |
885 | hr();
886 | ```
887 | 위와 같이 익명함수를 선언하여 사용할수도 있습니다. 쉼표가 중요합니다. `{{C(a, ', function() {},')}}`
888 |
889 | `H.each`, `S.each`
890 | ```javascript
891 | C(songs, [
892 | H('songs', '\
893 | h3 Sing Street OST\
894 | ul\
895 | !{C(songs, ', S.each('song, i', '\
896 | li {{i+1}}. {{song}}'),
897 | ')}!'),
898 | $,
899 | B.M('appendTo', 'body')]);
900 |
901 |
902 | hr();
903 | ```
904 |
905 |
906 | 템플릿 동작 순서
907 | 1. `!{}!` 실행 및 데이터 치환, 리턴 값을 H 문법으로 하고 싶을때
908 | 2. H to HTML
909 | 3. `{{{}}}` 실행 및 데이터 치환, 리턴 값을 HTML로 만들고 싶을때
910 | 4. `{{}}` 실행 및 데이터 치환, 리턴 값이 일반 문자열이어야 함
911 | `!{}!`, `{{}}`, `{{{}}}` 의 실행결과가 `.then` 인 경우 비동기를 기다렸다가 완성합니다.
912 | ```javascript
913 | var sum = CB(function (a, b, cb) {
914 | delay(function() {
915 | console.log(a + b);
916 | cb(a + b);
917 | });
918 | });
919 |
920 | C(songs, [
921 | H('songs', '\
922 | h3 Sing Street OST\
923 | ul\
924 | {{{C(songs, ', H.each('song, i', '\
925 | li {{C(i, 1, sum)}}. {{song}}'), // delay
926 | ')}}}'),
927 | $,
928 | B.M('appendTo', 'body'), // 2초 뒤 렌더링
929 | function() {
930 | $("html, body").animate({ scrollTop: $(window).scrollTop() + $(window).height() });
931 | }]);
932 | ```
933 |
934 |
935 | `S`는 일반 문자열을 만들때 사용합니다. `S`와 `S.each`가 있습니다.
936 | `H`, `H.each`는 HTML로 변환하여 리턴하고 `S`, `S.each`는 데이터 치환만한 문자열을 리턴합니다.
937 | ```javascript
938 | C({ id: 5, body: "foo bar" }, [
939 | _.values,
940 | C.toMR,
941 | S('id, body', "update posts set body = '{{body}}' where id = {{id}};"),
942 | function(query) {
943 | console.log(query);
944 | }]);
945 | ```
946 |
947 |
948 |
949 | ### 09. [IF ELSEIF ELSE](https://github.com/marpple/abc-functional-javascript/blob/master/example/09.%20IF%20ELSEIF%20ELSE.html)
950 | 자바스크립트에서 조건문을 작성할때 함수를 실행할 수 있는데 만일 그 함수가 비동기 함수라면 그렇게 할 수 없고 굉장히 로직이 복잡해지고 코딩하기 어려워집니다.
951 | 자바스크립트는 아래와 같이 코딩할 수 없어 이것을 자바스크립트의 단점으로 생각하기도 합니다.
952 | 하지만 이것에는 목적이 있습니다. 자바스크립트는 이벤트 루프를 이용하여 Non-Blocking IO을 지원하기 위해 아래 같은 상황에서 비동기가 일어납니다.
953 |
954 | ```javascript
955 | if (long_time(1)) {
956 | long_time(2);
957 | } else if (long_time(3)) {
958 | long_time(4);
959 | } else {
960 | long_time(5);
961 | }
962 | ```
963 | `long_time()` 함수의 return 값이 undefined 라면 위 코드는 long_time의 내부 구현과 관련 없이 무조건 `long_time(1)`과 `long_time(5)`만 실행될겁니다.
964 |
965 |
966 | abcjs는 비동기가 일어나는 상황에서도 위와 닮은 코딩을 할 수 있도록 `IF().ELSEIF().ELSE()` 를 지원합니다.
967 |
968 | ```javascript
969 | IF(
970 | 조건부 함수,
971 | 실행부 함수
972 | )
973 | ```
974 | `IF`와 `ELSEIF`는 첫번째 인자로 조건부 함수를 넘기고, 두번째 인자로 실행부 함수를 넘깁니다.
975 |
976 |
977 | ```javascript
978 | IF(
979 | 실행부 함수
980 | )
981 | ```
982 | 만약 위와 같이 실행부 함수만 넘길 경우 내부적으로 조건부 함수를 `function I(v) { return v; }` 로 채웁니다.
983 |
984 | `ELSE`는 실행부 함수만 넘깁니다.
985 |
986 | 조건부 함수와 실행부 함수 모두 []로 파이프라인을 만들 수 있습니다.
987 | ```javascript
988 | IF([p1, p2, p3], [f1, f2, f3])
989 | ```
990 |
991 | `IF().ELSEIF().ELSE()`는 chain 방식으로 function 하나를 리턴합니다. 조건부와 실행부 함수에게 최초 받은 인자들을 넘겨줍니다.
992 |
993 |
994 | ```javascript
995 | function f1() {
996 | console.log('f1')
997 | }
998 |
999 | function f2() {
1000 | console.log('f2')
1001 | }
1002 |
1003 | C(true, IF(f1).ELSE(f2));
1004 | // f1
1005 |
1006 | C(false, IF(f1).ELSE(f2));
1007 | // f2
1008 |
1009 | C(0,
1010 | IF(f1)
1011 | .ELSEIF(function(v) { return v === 0 },
1012 | function(v) {
1013 | console.log(v);
1014 | })
1015 | .ELSE(f2));
1016 | // 0
1017 |
1018 | C(5, 0,
1019 | IF(I, f1 // function I(v) { return v };
1020 | ).ELSEIF(function(a, b) { return a < b },
1021 | function(a, b) { console.log(a); }
1022 | ).ELSE(f2));
1023 | // f1
1024 |
1025 | C(0, 5,
1026 | IF(I, f1
1027 | ).ELSEIF(function(a, b) { return a > b },
1028 | function(a, b) { console.log(a); }
1029 | ).ELSE(f2));
1030 | // f2
1031 |
1032 | C(0, 5,
1033 | IF(I, f1
1034 | ).ELSEIF(_.negate(function(a, b) { return a > b }),
1035 | function(a, b) { console.log(a+b); }
1036 | ).ELSE(f2));
1037 | // 5
1038 | ```
1039 |
1040 |
1041 | 비동기 조건문
1042 | ```javascript
1043 | G["a < b long time"] = CB(function(a, b, cb) {
1044 | setTimeout(function() {
1045 | console.log('1 sec');
1046 | setTimeout(function() {
1047 | console.log('2 sec');
1048 | cb(a < b);
1049 | }, 1000)
1050 | }, 1000);
1051 | });
1052 |
1053 | C(0, 5,
1054 | IF(I, f1
1055 | ).ELSEIF(G["a < b long time"],
1056 | function(a, b) { console.log(a+b); }
1057 | ).ELSE(f2));
1058 | // 5
1059 | ```
1060 |
1061 | 실행부 역시 비동기제어가 가능하고 배열을 통해 파이프라인으로 만들 수 있습니다.
1062 | ```javascript
1063 | var square_long_time = CB(function(a, cb) {
1064 | setTimeout(function() {
1065 | cb(a * a);
1066 | }, 1000);
1067 | });
1068 |
1069 | C(0, 5, [
1070 | IF(I, f1
1071 | ).ELSEIF(G["a < b long time"],
1072 | [function(a, b) { return a + b; },
1073 | square_long_time]
1074 | ).ELSE(f2),
1075 | function(a) {
1076 | return a + 10;
1077 | },
1078 | function(r) {
1079 | console.log(r);
1080 | // 35
1081 | }]);
1082 | ```
1083 |
1084 |
1085 | abcjs를 이용하여 함수형 프로그래밍을 하면 비동기가 지원되는 `IF().ELSEIF().ELSE()` 같은 함수도 아래와 같이 쉽게 만들 수 있습니다.
1086 | 1. 최초 `IF`를 실행하면 `store`를 클로저로 생성하고
1087 | 2. chain 방식으로 이후에 `ELSEIF`와 `ELSE`가 실행 될때 마다 function set를 `store`에 모아놓고
1088 | 3. 리턴된 `IF`가 실행될때 `B.find`를 통해 실행해야하는 function set를 찾아 받아둔 `args`를 넘기며 실행합니다.
1089 |
1090 | 이미 `C`나 `B.find` 등이 이미 비동기를 잘 제어해주기 때문에 아래와 같이 callback 패턴 없이 동기 함수를 만들때와 완전히 똑같은 코딩을 할 수 있습니다.
1091 | ```javascript
1092 | // abc.js 459 line
1093 | function IF(predicate, fn) {
1094 | var store = [fn ? [predicate, fn] : [I, predicate]];
1095 | return _.extend(IF, {
1096 | ELSEIF: function (predicate, fn) {
1097 | return store.push(fn ? [predicate, fn] : [I, predicate]) && IF;
1098 | },
1099 | ELSE: function (fn) { return store.push([J(true), fn]) && IF; } });
1100 |
1101 | function IF() {
1102 | var args = arguments;
1103 | return C(store, args, [
1104 | B.find(function(fnset, i, l, args) { return A(args, fnset[0]); }),
1105 | function(fnset) { return fnset ? A(args, fnset[1]) : void 0; }
1106 | ]);
1107 | }
1108 | }
1109 | ```
1110 |
1111 |
1112 |
1113 | ### 10. [B.all B.spread](https://github.com/marpple/abc-functional-javascript/blob/master/example/09.%20IF%20ELSEIF%20ELSE.html)
1114 | `B.all` 함수는 같은 인자를 모든 파이프라인 혹은 함수에게 넘겨서 multiple results로 결과를 만드는 함수를 리턴합니다.
1115 | ```javascript
1116 | C(1, 5, [
1117 | B.all(
1118 | function(a, b) { return a + b; }, // a
1119 |
1120 | [function(a, b) { return a - b; },
1121 | function(a) { return a * a; }], // b
1122 |
1123 | function(a, b) { return MR(a, b); } // c, d (multiple results)
1124 | ),
1125 | function(a, b, c, d) {
1126 | console.log(a, b, c, d); // 6, 16, 1, 5
1127 | }]);
1128 | ```
1129 |
1130 |
1131 | `B.spread` 함수는 인자를 하나씩 모든 파이프라인 혹은 함수에게 나눠주고 multiple results로 결과를 만드는 함수를 리턴합니다.
1132 | ```javascript
1133 | C(1, 2, 3, 4, [
1134 | B.spread(
1135 | function(a) { return a + a; }, // a
1136 |
1137 | [function(a) { return a + a; },
1138 | function(a) { return a * a; }], // b
1139 |
1140 | function(a) { return MR(a, a - a); } // c, d (multiple results)
1141 |
1142 | // e ** 인자수보다 function의 갯수가 적을 경우 I로 채웁니다. function I(v) { return v }
1143 | ),
1144 | function(a, b, c, d, e) {
1145 | console.log(a, b, c, d, e); // 2, 16, 3, 0, 4
1146 | }]);
1147 |
1148 | C(1, 2, 3, 4, [
1149 | B.spread(
1150 | function(a) { return a + a; }, // a
1151 |
1152 | [function(a) { return a + a; },
1153 | function(a) { return a * a; }], // b
1154 |
1155 | function(a) { return MR(a, a - a); }, // c, d
1156 |
1157 | I, // e
1158 | I // f ** 인자수보다 function의 갯수가 많을 경우 인자는 undefined로 들어옵니다.
1159 | ),
1160 | function(a, b, c, d, e, f) {
1161 | console.log(a, b, c, d, e, f); // 2, 16, 3, 0, 4, undefined
1162 | }]);
1163 | ```
1164 |
1165 |
1166 | 결과를 배열로 받고 싶다면
1167 | ```javascript
1168 | C(1, 5, [
1169 | B.all(
1170 | function(a, b) { return a + b; }, // a
1171 |
1172 | [function(a, b) { return a - b; },
1173 | function(a) { return a * a; }], // b
1174 |
1175 | function(a, b) { return MR(a, b); } // c, d (multiple results)
1176 | ),
1177 | C.args,
1178 | C.toArray,
1179 | function(a) {
1180 | console.log(a); // [6, 16, 1, 5]
1181 | }]);
1182 | ```
1183 | Pipeline 패턴으로 코딩을 하다보면 `B.all`과 `B.spread` 같은 일을 하고 싶을때가 많습니다.
1184 | `B.all`과 `B.spread`에게 넘겨진 함수 혹은 Pipeline들은 하나씩 차례대로 실행됩니다.
1185 | 비동기가 일어나더라도 위에서 부터 하나씩 차례대로 실행됩니다.
1186 |
1187 | ### 11. [this](https://github.com/marpple/abc-functional-javascript/blob/master/example/11.%20this.html)
1188 | A, B, C 모두 this가 주어진다면 파이프라인안의 모든 함수에서 this를 이어줍니다.
1189 |
1190 |
1191 | #### A 함수로 this 전달하기
1192 | A 함수에서는 마지막 인자로 this를 받을 수 있습니다.
1193 | ```javascript
1194 | var r1 = A([1, 2], [
1195 | function(a, b) {
1196 | return a + b + this.c;
1197 | },
1198 | function(a) {
1199 | return a * this.c;
1200 | }
1201 | ], { c: 5 });
1202 | console.log(r1); // 40
1203 | ````
1204 |
1205 |
1206 | #### B 함수로 this 전달하기
1207 | B 함수는 함수를 리턴하는 함수입니다. B를 실행하여 리턴된 함수에 context를 넘겨주셔야합니다.
1208 | _.bind나 Function.prototype.bind 처럼 미리 this를 bind하는 기능은 없습니다.
1209 | 코어 자바스크립트에서의 this를 잘 이해하고 있다면 전혀 어렵지 않습니다.
1210 | 아래와 같은 케이스가 가능하겠습니다. 메소드 정의를 할때 유용합니다.
1211 | ```javascript
1212 | var user1 = {
1213 | firstName: "jamm",
1214 | lastName: "Co",
1215 | getName1: B(function() {
1216 | return this.lastName + ' ' + this.firstName;
1217 | }),
1218 | getName2: B([
1219 | B.all(function() {
1220 | return this.firstName;
1221 | }, function() {
1222 | return this.lastName;
1223 | }),
1224 | function(a, b) {
1225 | return a + ' ' + b;
1226 | }
1227 | ])
1228 | };
1229 |
1230 | console.log(user1.getName1()); // Co jamm
1231 | console.log(user1.getName2()); // jamm Co
1232 |
1233 | var same_age_friends = B([
1234 | function() {
1235 | return this.friends;
1236 | },
1237 | B.filter(function(friend) { return friend.age == this.me.age })
1238 | ]);
1239 |
1240 | var r2 = same_age_friends.call({
1241 | friends: [
1242 | { id: 1, name: "a", age: 10 },
1243 | { id: 2, name: "b", age: 12 },
1244 | { id: 3, name: "c", age: 12 },
1245 | { id: 4, name: "d", age: 13 }
1246 | ],
1247 | me: { id: 5, name: "e", age: 12 }
1248 | });
1249 |
1250 | console.log(JSON.stringify(r2)); // [{"id":2,"name":"b","age":12},{"id":3,"name":"c","age":12}]
1251 | ```
1252 |
1253 |
1254 | #### C 함수로 this 전달하기
1255 | ```javascript
1256 | var r3 = C.call({ c: 5 }, 1, 2, [
1257 | function(a, b) {
1258 | return a + b + this.c;
1259 | },
1260 | function(a) {
1261 | return a * this.c;
1262 | }
1263 | ]);
1264 | console.log(r3); // 40
1265 | ```
1266 |
1267 |
1268 | #### with jQuery
1269 | ```html
1270 |
1271 | ```
1272 | ```javascript
1273 | $(function() {
1274 | $('button').click(B([
1275 | CB(function(e, next) {
1276 | return $(this).animate({
1277 | 'margin-left': 300
1278 | }, 1000, next);
1279 | }),
1280 | function() {
1281 | $(this).text('finish');
1282 | }
1283 | ]));
1284 | });
1285 | // go --------------> finish
1286 | ```
1287 |
1288 |
1289 |
1290 | ### 12. ETC
1291 | 이 외에도 abcjs에는 B.args, B.m, C.val, F, G, C.u, C.val 등의 유용한 함수들이 있습니다.
1292 | - `B.args(n[,n,n...])` index가 n인 인자들 받기
1293 | - `B.m('method', 'args1', 'args2')` 객체의 메소드 실행하는 함수 뱉기
1294 | - `F('function.name') => G['function']['name']` 안전하게 function 찾기
1295 | - `F.A`, `F.B`, `F.C`, ... F 네임스페이스에 모든 함수 재추가
1296 | - `G = global || window`
1297 | - `C.u = function() {};`
1298 | - `C.val(user, 'friend.friends.0.name')` 안전하게 value 꺼내기
1299 |
1300 |
1301 | ### 13. [throw, ERR, CATCH](https://github.com/marpple/abc-functional-javascript/blob/master/example/13.%20CATCH.html)
1302 | ```javascript
1303 | C([
1304 | function() {
1305 | console.log(1);
1306 | },
1307 | function() {
1308 | console.log(2);
1309 | throw 2;
1310 | },
1311 | function() {
1312 | console.log(3);
1313 | },
1314 | CATCH(function(e) {
1315 | console.log(4, e);
1316 | })]);
1317 | // 1
1318 | // 2
1319 | // 4, Error: 2(…)
1320 | console.log('-------------------------');
1321 |
1322 | C([
1323 | function() {
1324 | console.log(1);
1325 | },
1326 | function() {
1327 | console.log(2);
1328 | return ERR(2);
1329 | },
1330 | function() {
1331 | console.log(3);
1332 | },
1333 | CATCH(function(e) {
1334 | console.log(4, e);
1335 | }),
1336 | function() {
1337 | console.log(5);
1338 | }]);
1339 | // 1
1340 | // 2
1341 | // 4, Error: 2(…)
1342 | // 5
1343 | console.log('-------------------------');
1344 |
1345 | C([
1346 | function() {
1347 | console.log(1);
1348 | return ERR('custom data', {
1349 | type: 1,
1350 | msg: 'hi'
1351 | });
1352 | },
1353 | CATCH(function(e) {
1354 | if (e.type == 1) {
1355 | console.log(e.msg, e);
1356 | } else {
1357 | console.log('else');
1358 | }
1359 | })]);
1360 | // 1
1361 | // hi Error: custom data(…)
1362 | console.log('-------------------------');
1363 |
1364 | C([
1365 | function() {
1366 | console.log(1);
1367 | },
1368 | function() {
1369 | console.log(2);
1370 | },
1371 | function() {
1372 | console.log(3);
1373 | },
1374 | CATCH(function(e) {
1375 | console.log(4, e);
1376 | }),
1377 | function() {
1378 | console.log(5);
1379 | }]);
1380 |
1381 | // 1
1382 | // 2
1383 | // 3
1384 | // 5
1385 | console.log('-------------------------');
1386 |
1387 | C([
1388 | function() {
1389 | console.log(1);
1390 | },
1391 | function() {
1392 | return C([
1393 | function() {
1394 | console.log(2);
1395 | },
1396 | function() {
1397 | console.log(3);
1398 | throw 3
1399 | },
1400 | function() {
1401 | console.log(4);
1402 | },
1403 | CATCH(function(e) {
1404 | console.log(5, e);
1405 | }),
1406 | function() {
1407 | console.log(6);
1408 | },
1409 | function() {
1410 | console.log(7);
1411 | return ERR(7);
1412 | }
1413 | ])
1414 | },
1415 | function() {
1416 | console.log(8);
1417 | },
1418 | CATCH(function(e) {
1419 | console.log(9, e);
1420 | return 'hi'
1421 | }),
1422 | function(a) {
1423 | console.log(10, a);
1424 | }
1425 | ]);
1426 | // 1
1427 | // 2
1428 | // 3
1429 | // 5 Error: 3(…)
1430 | // 6
1431 | // 7
1432 | // 9 Error: 7(…)
1433 | // 10 'hi'
1434 |
1435 | console.log('-------------------------');
1436 |
1437 | // for async
1438 | var go = B([
1439 | function(a) {
1440 | console.log(1);
1441 | return a;
1442 | },
1443 | CB(function(a, next) {
1444 | console.log(2);
1445 | setTimeout(function() {
1446 | next(a == 1 ? 2 : ERR(2));
1447 | }, 500)
1448 | }),
1449 | function() {
1450 | console.log(3);
1451 | },
1452 | function() {
1453 | console.log(4);
1454 | return 'complete';
1455 | },
1456 | CB(CATCH(function(e, next) {
1457 | console.log(5, e);
1458 | setTimeout(function() {
1459 | // rollback
1460 | next('fail');
1461 | }, 1000);
1462 | })),
1463 | function(a) {
1464 | console.log(a);
1465 | console.log('------------------')
1466 | }
1467 | ]);
1468 |
1469 | go(1).then(function() {
1470 | // 1
1471 | // 2
1472 | // 3
1473 | // 4
1474 | // complete
1475 |
1476 | go(2);
1477 | // 1
1478 | // 2
1479 | // 5 Error: 2(…)
1480 | // fail
1481 | });
1482 | ```
1483 |
1484 | __이제 재밌는 함수 조립을 즐겨보세요! :smile:__
1485 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | # abcjs
2 | _abcjs is functional javascript library._
3 |
4 |
5 | ## Summary
6 | - It can be used in Web browser and NodeJS.
7 | - It supports asynchronous control simpler and handier than ‘Promise’ or ‘Monad’.
8 | - Even if the function is ‘asynchronous’, abc.js can make a logic structure as we make a ‘synchronous’ function.
9 | - There are functions which added the ‘asynchronous control’ feature : each, map, reduce, filter, reject, find, some, every, uniq
10 | - abc.js provides a HTML template engine of which grammar and function is similar to Jade.
11 | - abc.js provides functions which write SQL query easier.
12 | - The line of script file of abc.js is just 808 lines. Moreover, it doesn’t have a dependency to another javascript library.
13 | - abc.js respect underscore.js concept!
14 |
15 | ## Table of Contents
16 | - [A (apply)](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#01-a)
17 | - [B (bind)](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#02-b)
18 | - [C (call)](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#03-c)
19 | - [Pipeline with ABC](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#04-pipeline-with-abc)
20 | - [Async (callback)](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#05-asynccallback)
21 | - [Async-2 (Promise)](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#06-async-2promise)
22 | - [each, map, find, ...](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#07-eachmapfind)
23 | - [HTML Template](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#08-html-template)
24 | - [IF ELSEIF ELSE](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#09-if-elseif-else)
25 | - [B.all B.spread](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#10-ball-bdiv)
26 | - [this](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#11-this)
27 | - [ETC](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#12-etc)
28 | - [throw, ERR, CATCH](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md#13-throw-err-catch)
29 |
30 | ### 01. [A](https://github.com/marpple/abc-functional-javascript/blob/master/README_EN.md/blob/master/example/01.%20A.html)
31 | `A` is similar to `apply`.
32 |
33 | ```javascript
34 | function add(a, b) {
35 | return a + b;
36 | }
37 |
38 | var r1 = A([20, 2], add); // add.apply(undefined, [20, 2]);
39 | console.log(r1);
40 | // 22
41 | ```
42 | When reading a code, It is easy to read from left to right in general.
43 |
44 | So, `A` changed the direction
45 | `data argument`, `function arguments`... :
46 |
47 | In `A` function, argument which is not a function appears first, argument which is a function appears later.
48 | ```javascript
49 | function minus(a, b) {
50 | return a - b;
51 | }
52 |
53 | var r2 = A([20, 5], function() {
54 | return A(arguments, minus);
55 | });
56 | console.log(r2);
57 | // 15
58 | ```
59 |
60 |
61 | ### 02. [B](https://github.com/marpple/abc-functional-javascript/blob/master/example/02.%20B.html)
62 | `B` is similar to `bind` and `_.partial` of underscore, but it is thisless.
63 |
64 | ```javascript
65 | function minus(a, b) {
66 | return a - b;
67 | }
68 |
69 | var f1 = B(10, minus);
70 | var r1 = f1(20);
71 | console.log(r1);
72 | // -10
73 | ```
74 |
75 |
76 | Use `X` with `B`.
77 |
78 | Through `X`, it can be possible to set a position of argument that will be populated after an execution.
79 | ```javascript
80 | var f2 = B(X, 10, minus);
81 | var r2 = f2(20);
82 | console.log(r2);
83 | // 10
84 |
85 | function minus2(a, b, c, d) {
86 | return a - b - c - d;
87 | }
88 |
89 | var f3 = B(4, X, X, 1, minus2);
90 | var r3 = f3(3, 2);
91 | console.log(r3);
92 | // -2
93 | ```
94 |
95 |
96 |
97 | ### 03. [C](https://github.com/marpple/abc-functional-javascript/blob/master/example/03.%20C.html)
98 | `C` is similar to `call` without `this`.
99 | ```javascript
100 | function minus(a, b) {
101 | return a - b;
102 | }
103 |
104 | var r1 = C(10, 20, minus);
105 | console.log(r1);
106 | // -10
107 |
108 | var r2 = C(20, 10, minus);
109 | console.log(r2);
110 | // 10
111 | ```
112 |
113 |
114 |
115 | ### 04. [Pipeline with ABC](https://github.com/marpple/abc-functional-javascript/blob/master/example/04.%20Pipeline%20with%20ABC.html)
116 | abcjs supports code pattern that looks like underscore’s `_.compose`, Jquery’s chain.
117 |
118 | Object Oriented Chaining could change a value sequentially.
119 |
120 | However, since it changes the value of the object itself, implementation is hard, although the use is easy.
121 |
122 | Moreover, it has some restriction , because it only uses the method and the value of object itself.
123 |
124 | Instead, abcjs uses `Pipeline-like` sequential function execution.
125 |
126 | This `Pipeline-like` approach has advantages :
127 | - more flexible
128 | - can make a `pure function`.
129 |
130 | abc.js didn’t make a special pipeline function.
131 |
132 | You can use instantly pipeline pattern with `A`, `B`, and `C` function.
133 |
134 | Furthermore, unlike `_.compose` , we changed the order which is easy to read.
135 |
136 | It is easy : Put the array which has the functions as a last argument.
137 |
138 | ```javascript
139 | function sum(a, b) {
140 | return a + b;
141 | }
142 |
143 | function square(a) {
144 | return a * a;
145 | }
146 |
147 | var r1 = C(1, 2, [
148 | sum,
149 | square,
150 | square,
151 | function(a) {
152 | console.log(a); // 81
153 | return a - 20;
154 | }]);
155 |
156 | console.log(r1);
157 | // 61
158 | ```
159 | arguments `1,2` is passed into `sum`.
160 |
161 | And the result of `sum` is passed to `square`, and so on ;
162 |
163 | The result of function is passed as an arugument of next function.
164 |
165 | Finally, the returned value of the last function will be the result of `C` execution.
166 |
167 | Through `A`, You can use Pipeline pattern with array and arguments object.
168 | ```javascript
169 | A([1, 2], [
170 | sum,
171 | square,
172 | square,
173 | function(a) {
174 | return a - 20;
175 | },
176 | function(r1) {
177 | console.log(r1);
178 | // 61
179 | }]);
180 | ```
181 | Through `B`, you can build ‘big’ function by gathering ‘small’ functions.
182 |
183 | Or, you can split ‘big’ function into small unit.
184 |
185 | By assembling with small functions, code reusability increases.
186 |
187 | ```javascript
188 | var f1 = B([
189 | sum,
190 | square,
191 | square,
192 | function(a) {
193 | console.log(a); // 81
194 | return a - 20;
195 | }]);
196 |
197 | var r3 = f1(1, 2);
198 | console.log(r3);
199 | // 61
200 | ```
201 | In the combination of functions,
202 |
203 | with `X`, it can be possible to set a position of argument that will be populated after an execution.
204 |
205 | ```javascript
206 | function minus(a, b) {
207 | return a - b;
208 | }
209 |
210 | var f2 = B(X, 10, [
211 | f1,
212 | B(X, 11, minus)
213 | ]);
214 |
215 | var r4 = f2(-7);
216 | console.log(r4);
217 | // 50
218 | ```
219 |
220 |
221 | This Supports Multiple results by `MR`.
222 |
223 | ```javascript
224 | C(3, 2, [
225 | function(a, b) {
226 | return MR(a + b, a - b, a * b); // multiple results
227 | },
228 | function(a, b, c) {
229 | console.log(a, b, c);
230 | return MR(a, c); // multiple results
231 | },
232 | function(a, c) {
233 | console.log(a, c);
234 | return arguments;
235 | },
236 | C.toMR, // arguments to multiple results
237 | function(a, c) {
238 | console.log(a, c); // 5, 6
239 | }]);
240 | ```
241 |
242 | ```javascript
243 | var minus = B(function(a, b) {
244 | console.log(a, b); // 20, 10
245 | return a - b;
246 | });
247 |
248 | var r5 = minus(10, 20);
249 | console.log(r5); // -10
250 |
251 | var swap = function(a, b) {
252 | return MR(b, a);
253 | };
254 | var r6 = minus(swap(10, 20));
255 | console.log(r6);
256 | // 10
257 | ```
258 |
259 | `MR` is similar to `return` of Go.
260 | [Go Lang - Multiple Results](https://tour.golang.org/basics/6)
261 |
262 |
263 | ```javascript
264 | var difference = B([
265 | function(a, b) {
266 | return MR(Math.max(a, b), Math.min(a, b));
267 | },
268 | minus
269 | ]);
270 |
271 | var r7 = difference(10, 20);
272 | console.log(r7);
273 | // 10
274 |
275 | var r8 = difference(30, 10);
276 | console.log(r8);
277 | // 20
278 | ```
279 |
280 |
281 | ```javascript
282 | var difference2 = B([
283 | C.args, // function() { return arguments },
284 | _.toArray,
285 | B.M('sort'), // function(a) { return a.sort(); },
286 | B.M('reverse'), // function(a) { return a.reverse(); },
287 | C.toMR, // array to multiple results
288 | minus]);
289 |
290 | var r9 = difference2(10, 20);
291 | console.log(r9);
292 | // 10
293 |
294 | var r10 = difference2(30, 10);
295 | console.log(r10);
296 | // 20
297 |
298 | var difference3 = B([minus, Math.abs]);
299 |
300 | var r11 = difference3(10, 20);
301 | console.log(r11);
302 | // 10
303 |
304 | var r12 = difference3(30, 10);
305 | console.log(r12);
306 | // 20
307 | ```
308 |
309 |
310 |
311 | ### 05. [Async(callback)](https://github.com/marpple/abc-functional-javascript/blob/master/example/05.%20Async%20(callback).html)
312 | 'A' offers a variety of features related to asynchronous.
313 |
314 | Async with Pipeline.
315 |
316 | ```javascript
317 | function delay(func) {
318 | setTimeout(function() {
319 | func();
320 | }, 1000);
321 | }
322 |
323 | function sum(a, b, cb) {
324 | delay(function() {
325 | cb(a + b);
326 | });
327 | }
328 |
329 | function minus(a, b, cb) {
330 | delay(function() {
331 | cb(a - b);
332 | });
333 | }
334 |
335 | function square(a, cb) {
336 | delay(function() {
337 | cb(a * a);
338 | });
339 | }
340 |
341 | sum(10, 20, function (r) {
342 | console.log(r); // 30
343 | });
344 |
345 | minus(10, 7, function(r) {
346 | console.log(r); // 3
347 | });
348 |
349 | sum(5, 7, function(r) {
350 | minus(r, 5, function(r) {
351 | square(r, function(r) {
352 | console.log(r); // 49
353 | });
354 | });
355 | });
356 | ```
357 |
358 | ```javascript
359 | CB(sum, minus, square);
360 |
361 | C(5, 10, [
362 | sum,
363 | CB(function(r, cb) {
364 | minus(r, 5, cb);
365 | }),
366 | square,
367 | function(r) {
368 | console.log(r); // 100
369 | }
370 | ]);
371 | ```
372 |
373 | ```javascript
374 | .then(function() {
375 | return new Promise(function(rs) {
376 | sum(5, 10, rs)
377 | });
378 | }).then(function() {
379 | return new Promise(function(rs) {
380 | minus(5, 10, rs)
381 | });
382 | });
383 | ```
384 |
385 | So Simple!!
386 |
387 | Multiple results
388 | ```javascript
389 | C(5, 9, [
390 | sum,
391 | CB(function(a, cb) {
392 | delay(function() {
393 | cb(a, 10); // auto multiple results
394 | });
395 | }),
396 | minus,
397 | function(r) {
398 | console.log(r); // 4
399 | }
400 | ]);
401 | ```
402 |
403 |
404 | ```javascript
405 | var $ = {};
406 |
407 | $.get = function(url, cb) {
408 | delay(function() {
409 | cb({
410 | a: 5,
411 | b: 3
412 | });
413 | });
414 | };
415 |
416 | $.post = function(url, data, cb) {
417 | delay(function() {
418 | cb(_.extend(data, { created_at: new Date() }));
419 | });
420 | };
421 |
422 | $.put = function(url, data, cb) {
423 | delay(function() {
424 | cb(_.extend(data, { updated_at: new Date() }));
425 | });
426 | };
427 | ```
428 | `$.get`, `$.post`.
429 | ```javascript
430 | C([
431 | CB(function(cb) {
432 | $.get("/get_data", cb);
433 | },
434 | function(data, cb) {
435 | console.log(_.clone(data)); // {a: 5, b: 3}
436 | $.post("/post_data", _.extend(data, { c: 10 }), cb);
437 | },
438 | function(data, cb) {
439 | console.log(_.clone(data)); // {a: 5, b: 3, c: 10, created_at: Tue Sep 13 2016 04:01:19 GMT+0900 (KST)}
440 | $.put("/put_data", _.extend(data, { c: 5 }), cb);
441 | }),
442 | function(r) {
443 | console.log(r);
444 | // {a: 5, b: 3, c: 5, created_at: Tue Sep 13 2016 04:03:58 GMT+0900 (KST), updated_at: Tue Sep 13 2016 04:03:59 GMT+0900 (KST)}
445 | }
446 | ]);
447 | ```
448 |
449 | `B` is similar to `_.partial`. Function is last argument.
450 | ```javascript
451 | CB($.get, $.post, $.put);
452 |
453 | C([
454 | B("/get_data", $.get),
455 | B({ c: 20 }, _.extend),
456 | B("/post_data", $.post),
457 | B({ c: 30 }, _.extend),
458 | B("/put_data", $.put),
459 | function(r) {
460 | console.log(r);
461 | // {c: 20, a: 5, b: 3, created_at: Tue Sep 13 2016 04:01:19 GMT+0900 (KST), updated_at: Tue Sep 13 2016 04:01:20 GMT+0900 (KST)}
462 | }
463 | ]);
464 | ```
465 |
466 | ```javascript
467 | function J(v) {
468 | return function() {
469 | return v;
470 | }
471 | }
472 | ```
473 |
474 | ```javascript
475 | C([
476 | J(MR("/post_data", { aka: 'Cojamm' })),
477 | $.post,
478 | function(r) {
479 | console.log(r); // {aka: "Cojamm", created_at: Tue Sep 13 2016 04:01:18 GMT+0900 (KST)}
480 | }
481 | ]);
482 | ```
483 |
484 | ```javascript
485 | function sum(a, b, cb) {
486 | delay(function() {
487 | cb(a + b);
488 | });
489 | }
490 | function minus(a, b, cb) {
491 | delay(function() {
492 | cb(a - b);
493 | });
494 | }
495 | function square(a, cb) {
496 | delay(function() {
497 | cb(a * a);
498 | });
499 | }
500 | var sq = B(square);
501 | var m5 = B(X, 5, minus);
502 | var s = B(sum);
503 | var log = B(function() {
504 | console.log.apply(console, arguments);
505 | });
506 | log(sq(m5(s(10, 10))));
507 | console.log('216 line');
508 | ```
509 |
510 |
511 | ### 06. [Async-2(Promise)](https://github.com/marpple/abc-functional-javascript/blob/master/example/06.%20Async-2%20(Promise).html)
512 | abcjs supports Promise.
513 |
514 | ```javascript
515 | function delay() {
516 | return new Promise(function(rs) {
517 | setTimeout(function() {
518 | rs();
519 | }, 1000);
520 | })
521 | }
522 |
523 | function sum(a, b) {
524 | return delay().then(function() {
525 | return a + b;
526 | });
527 | }
528 |
529 | function minus(a, b) {
530 | return delay().then(function() {
531 | return a - b;
532 | });
533 | }
534 |
535 | function square(a) {
536 | return delay().then(function() {
537 | return a * a;
538 | });
539 | }
540 |
541 | // Promise
542 | sum(2, 4)
543 | .then(function(r) {
544 | return minus(r, 4);
545 | }).then(function(r) {
546 | return square(r);
547 | }).then(function(r) {
548 | console.log(r); // 4
549 | });
550 |
551 | // ABC
552 | C(3, 6, [
553 | sum,
554 | function(r) {
555 | return minus(r, 5);
556 | },
557 | square,
558 | function(r) {
559 | console.log(r); // 16
560 | }]);
561 | ```
562 |
563 | ```javascript
564 | C(5, 5, [
565 | sum,
566 | square
567 | ]).then(function(r) {
568 | console.log(r); // 100
569 | });
570 |
571 | sum(2, 4)
572 | .then(function(r) {
573 | return C(r, 2, [
574 | sum,
575 | square
576 | ]);
577 | }).then(function(r) {
578 | console.log(r); // 64
579 | });
580 |
581 | sum(2, 4)
582 | .then(B(X, 2, [
583 | sum,
584 | square
585 | ])).then(function(r) {
586 | console.log(r); // 64
587 | });
588 |
589 | C(5, 5, [
590 | sum,
591 | square,
592 | CB(function(r, cb) {
593 | cb(r / 2)
594 | })
595 | ]).then(function(r) {
596 | console.log(r); // 50
597 | });
598 | ```
599 | `function hasPromise() { (window || global).Promise.prototype.then; }`
600 | `hasPromise() ? new Promise(function(rs) { resolve = rs; }) : { then: function(rs) { resolve = rs; } })`
601 |
602 |
603 |
604 | ### 07. [each...map...find...](https://github.com/marpple/abc-functional-javascript/blob/master/example/07.%20each...map...find....html)
605 | 1. Possible to put more arguments in addition to the array or object.
606 | - ex) [1, 2, 3], arg1, arg2, arg3 => value, key, list, arg1, arg2, arg3 ...
607 | 2. Async.
608 | 3. Pipeline iteratee, Pipeline predicate.
609 | 4. `C.map` is general. `B.map` concept is _.partial.
610 | 5. thisless.
611 |
612 | ```javascript
613 | function delay(func) {
614 | setTimeout(function() {
615 | func();
616 | }, 1000);
617 | }
618 |
619 | function sum(a, b) {
620 | return a + b;
621 | }
622 |
623 | function square(a) {
624 | return a * a;
625 | }
626 |
627 | C.each([1, 2, 3], function(v) {
628 | console.log(v);
629 | });
630 | // 1
631 | // 2
632 | // 3
633 |
634 | var r1 = C.map([1, 2, 3], square);
635 | console.log(r1); // [1, 4, 9]
636 |
637 | /* Pipeline iteratee */
638 | var r2 = C.map({ a: 1, b: 2, c: 3 }, [I, square]);
639 | console.log(r2); // [1, 4, 9]
640 |
641 | var r3 = C.map([1, 2, 3], 5, function(v, i, l, a) { //val, idx, list, 5
642 | return sum(v, a);
643 | });
644 | console.log(r3); // [6, 7, 8]
645 |
646 | /* B.args(0, 3) => value, 5 */
647 | var r4 = C.map([1, 2, 3], 5, [B.args(0, 3), sum]);
648 | console.log(r4); // [6, 7, 8]
649 |
650 |
651 | var r6 =
652 | C({ a: 1, b: 2, c: 3 }, [
653 | B.map(I), // [1, 2, 3] // function I(v) { return v; }
654 | B.map(square), // [1, 4, 9]
655 | function(v) { return MR(v, 0); },
656 | B.reduce(function(memo, v) {
657 | return memo + v;
658 | })]);
659 | console.log(r6); // 14
660 |
661 | var minus = function(a, b) {
662 | return a - b;
663 | };
664 |
665 | C({ a: 1, b: 2, c: 3 }, [
666 | B.map(I), // [1, 2, 3]
667 | B.map(square), // [1, 4, 9]
668 | function(v) { return MR(v, 0); },
669 | B.reduce([B.args(0, 1), minus]),
670 | function(r7) {
671 | console.log(r7); // -14
672 | }]);
673 |
674 | var minus2 = CB(function(a, b, cb) {
675 | delay(function() {
676 | cb(a - b);
677 | });
678 | });
679 | ```
680 |
681 |
682 | No need to modify the code for asynchronous.
683 | ```javascript
684 | C({ a: 1, b: 2, c: 3 }, [
685 | B.map(I), // [1, 2, 3]
686 | B.map(square), // [1, 4, 9]
687 | function(v) { return MR(v, 0); },
688 | B.reduce([B.args(0, 1), minus2]), // Async
689 | function(r7) {
690 | console.log(r7); // -14
691 | }]);
692 |
693 | var users = [
694 | { id: 1, age: 20, activated: true },
695 | { id: 2, age: 20, activated: false },
696 | { id: 3, age: 31, activated: false },
697 | { id: 4, age: 32, activated: true },
698 | { id: 5, age: 17, activated: true },
699 | { id: 6, age: 32, activated: true }
700 | ];
701 |
702 | C(users, [
703 | B.filter(B.v('activated')),
704 | B.reject(function(user) {
705 | return user.age > 30;
706 | }),
707 | B.map(B.v('age')),
708 | function(r8) {
709 | console.log(r8); // [20, 17]
710 | }]);
711 |
712 | C(users, [
713 | B.uniq('$.age'),
714 | B.map(B.v('id')),
715 | function(r9) {
716 | console.log(r9); // [1, 3, 4, 5]
717 | }]);
718 |
719 | C(users, [
720 | G[":reject :age > 30"] = B.reject(function(user) {
721 | return user.age > 30;
722 | }),
723 | B.every(B.v('activated')),
724 | function(r10) {
725 | console.log(r10); // false
726 | }]);
727 |
728 | C(users, [
729 | G[":reject :age > 30"],
730 | B.some(B.v('activated')),
731 | function(r11) {
732 | console.log(r11); // true
733 | }]);
734 | ```
735 |
736 | Functions for Array and Object.
737 | `C.each`, `C.map`, `C.reduce`, `C.filter`, `C.reject`, `C.find`, `C.find_index`, `C.some`, `C.every`, `C.uniq`,
738 | `B.each`, `map`, `B.reduce`, `B.filter`, `B.reject`, `B.find`, `B.find_index`, `B.some`, `B.every`, `B.uniq`
739 |
740 |
741 |
742 | ### 08. [HTML Template](https://github.com/marpple/abc-functional-javascript/blob/master/example/08.%20HTML%20Template.html)
743 |
744 | Support Async.
745 |
746 | ```javascript
747 | C([
748 | H('', '\
749 | .member[style="border: 1px solid #000; padding: 20px;"]\
750 | h3 People\
751 | ul\
752 | li Cojamm\
753 | li BJ\
754 | li JM\
755 | li PJ\
756 | li HA\
757 | li JE\
758 | #id1.class1.class2[class=class3] hi\
759 | .service\
760 | a[href=http://www.marpple.com target=_blank] http://www.marpple.com\
761 | textarea[rows=10].\
762 | Custom T-Shirts\
763 | \
764 | \
765 | Design Platform\
766 | Artwork\
767 | br\
768 | p hello\
769 | textarea\
770 | | foo bar\
771 | | hello world'),
772 | $,
773 | B.M('appendTo', 'body')]);
774 |
775 |
776 | hr();
777 | ```
778 |
779 |
780 | ```javascript
781 | C({ name: 'Cojamm', age: 32 }, [
782 | H('user', '\
783 | .person\
784 | .name {{user.name}}\
785 | .age {{user.age}}'),
786 | $,
787 | B.M('appendTo', 'body')]);
788 |
789 | hr();
790 |
791 | var post = {
792 | body: '하이랩이나 잘하라고',
793 | name: 'Cojamm',
794 | created_at: new Date()
795 | };
796 |
797 | C(post, [
798 | H('post', '\
799 | .name name: {{post.name}}\
800 | .body body: {{post.body}}\
801 | .created_at created at: {{post.created_at}}'),
802 | $,
803 | B.M('appendTo', 'body')]);
804 |
805 | hr();
806 |
807 | delete post.name;
808 |
809 | window.moment_lll = B([moment, B.M('format', 'lll')]);
810 |
811 | C(post, [
812 | H('post', '\
813 | .name name: {{post.name || "anonymous"}}\
814 | .body body: {{{post.body}}}\
815 | .created_at created at: {{moment_lll(post.created_at)}}'),
816 | $,
817 | B.M('appendTo', 'body')]);
818 |
819 | hr();
820 | ```
821 |
822 |
823 | You can use function.
824 | ```javascript
825 | var songs = [
826 | 'The Riddle Of The Model',
827 | 'Up',
828 | 'To Find You',
829 | 'A Beautiful Sea',
830 | 'Drive It Like You Stole It',
831 | 'Up (Bedroom Mix)',
832 | 'Girls',
833 | 'Brown Shoes'];
834 |
835 | C(songs, [
836 | H('songs', '\
837 | h3 Sing Street OST\
838 | ul\
839 | {{{C(songs, ', function(songs) {
840 | return _.map(songs, function(song, i) {
841 | return '
' + (i+1) + '. ' + song + '
';
842 | }).join("");
843 | },')}}}'),
844 | $,
845 | B.M('appendTo', 'body')]);
846 |
847 | hr();
848 | ```
849 | `{{C(a, ', function() {},')}}`
850 |
851 | `H.each`, `S.each`
852 | ```javascript
853 | C(songs, [
854 | H('songs', '\
855 | h3 Sing Street OST\
856 | ul\
857 | !{C(songs, ', S.each('song, i', '\
858 | li {{i+1}}. {{song}}'),
859 | ')}!'),
860 | $,
861 | B.M('appendTo', 'body')]);
862 |
863 |
864 | hr();
865 | ```
866 |
867 |
868 | Process
869 | 1. `!{}!`
870 | 2. H to HTML
871 | 3. `{{{}}}`
872 | 4. `{{}}`
873 |
874 | ```javascript
875 | var sum = CB(function (a, b, cb) {
876 | delay(function() {
877 | console.log(a + b);
878 | cb(a + b);
879 | });
880 | });
881 |
882 | C(songs, [
883 | H('songs', '\
884 | h3 Sing Street OST\
885 | ul\
886 | {{{C(songs, ', H.each('song, i', '\
887 | li {{C(i, 1, sum)}}. {{song}}'), // delay
888 | ')}}}'),
889 | $,
890 | B.M('appendTo', 'body'),
891 | function() {
892 | $("html, body").animate({ scrollTop: $(window).scrollTop() + $(window).height() });
893 | }]);
894 | ```
895 |
896 |
897 | SQL
898 | ```javascript
899 | C({ id: 5, body: "foo bar" }, [
900 | _.values,
901 | C.toMR,
902 | S('id, body', "update posts set body = '{{body}}' where id = {{id}};"),
903 | function(query) {
904 | console.log(query);
905 | }]);
906 | ```
907 |
908 |
909 |
910 | ### 09. [IF ELSEIF ELSE](https://github.com/marpple/abc-functional-javascript/blob/master/example/09.%20IF%20ELSEIF%20ELSE.html)
911 |
912 | If long_time is async function, it is difficult to write the following code:
913 |
914 | ```javascript
915 | if (long_time(1)) {
916 | long_time(2);
917 | } else if (long_time(3)) {
918 | long_time(4);
919 | } else {
920 | long_time(5);
921 | }
922 | ```
923 | If `long_time(1)` result is undefined, it operates only `long_time(1)` and `long_time(5)`.
924 |
925 | `IF().ELSEIF().ELSE()` works well in an asynchronous.
926 |
927 | ```javascript
928 | IF(
929 | predicate,
930 | body
931 | )
932 | ```
933 |
934 |
935 | ```javascript
936 | IF(
937 | body
938 | )
939 |
940 | // same IF(_.identity, body)
941 |
942 | ```
943 |
944 | argument of `ELSE` is only body.
945 |
946 | Supports pipeline.
947 | ```javascript
948 | IF([p1, p2, p3], [f1, f2, f3])
949 | ```
950 |
951 | ```javascript
952 | function f1() {
953 | console.log('f1')
954 | }
955 |
956 | function f2() {
957 | console.log('f2')
958 | }
959 |
960 | C(true, IF(f1).ELSE(f2));
961 | // f1
962 |
963 | C(false, IF(f1).ELSE(f2));
964 | // f2
965 |
966 | C(0,
967 | IF(f1)
968 | .ELSEIF(function(v) { return v === 0 },
969 | function(v) {
970 | console.log(v);
971 | })
972 | .ELSE(f2));
973 | // 0
974 |
975 | C(5, 0,
976 | IF(I, f1 // function I(v) { return v };
977 | ).ELSEIF(function(a, b) { return a < b },
978 | function(a, b) { console.log(a); }
979 | ).ELSE(f2));
980 | // f1
981 |
982 | C(0, 5,
983 | IF(I, f1
984 | ).ELSEIF(function(a, b) { return a > b },
985 | function(a, b) { console.log(a); }
986 | ).ELSE(f2));
987 | // f2
988 |
989 | C(0, 5,
990 | IF(I, f1
991 | ).ELSEIF(_.negate(function(a, b) { return a > b }),
992 | function(a, b) { console.log(a+b); }
993 | ).ELSE(f2));
994 | // 5
995 | ```
996 |
997 |
998 | Async
999 | ```javascript
1000 | G["a < b long time"] = CB(function(a, b, cb) {
1001 | setTimeout(function() {
1002 | console.log('1 sec');
1003 | setTimeout(function() {
1004 | console.log('2 sec');
1005 | cb(a < b);
1006 | }, 1000)
1007 | }, 1000);
1008 | });
1009 |
1010 | C(0, 5,
1011 | IF(I, f1
1012 | ).ELSEIF(G["a < b long time"],
1013 | function(a, b) { console.log(a+b); }
1014 | ).ELSE(f2));
1015 | // 5
1016 | ```
1017 |
1018 | ```javascript
1019 | var square_long_time = CB(function(a, cb) {
1020 | setTimeout(function() {
1021 | cb(a * a);
1022 | }, 1000);
1023 | });
1024 |
1025 | C(0, 5, [
1026 | IF(I, f1
1027 | ).ELSEIF(G["a < b long time"],
1028 | [function(a, b) { return a + b; },
1029 | square_long_time]
1030 | ).ELSE(f2),
1031 | function(a) {
1032 | return a + 10;
1033 | },
1034 | function(r) {
1035 | console.log(r);
1036 | // 35
1037 | }]);
1038 | ```
1039 |
1040 |
1041 | In order to implement the `IF().ELSEIF().ELSE()`, a separate code for asynchronous was not necessary at all.
1042 |
1043 | ```javascript
1044 | // abc.js 459 line
1045 | function IF(predicate, fn) {
1046 | var store = [fn ? [predicate, fn] : [I, predicate]];
1047 | return _.extend(IF, {
1048 | ELSEIF: function (predicate, fn) {
1049 | return store.push(fn ? [predicate, fn] : [I, predicate]) && IF;
1050 | },
1051 | ELSE: function (fn) { return store.push([J(true), fn]) && IF; } });
1052 |
1053 | function IF() {
1054 | var args = arguments;
1055 | return C(store, args, [
1056 | B.find(function(fnset, i, l, args) { return A(args, fnset[0]); }),
1057 | function(fnset) { return fnset ? A(args, fnset[1]) : void 0; }
1058 | ]);
1059 | }
1060 | }
1061 | ```
1062 |
1063 |
1064 |
1065 | ### 10. [B.all B.spread](https://github.com/marpple/abc-functional-javascript/blob/master/example/09.%20IF%20ELSEIF%20ELSE.html)
1066 | `B.all`
1067 | ```javascript
1068 | C(1, 5, [
1069 | B.all(
1070 | function(a, b) { return a + b; }, // a
1071 |
1072 | [function(a, b) { return a - b; },
1073 | function(a) { return a * a; }], // b
1074 |
1075 | function(a, b) { return MR(a, b); } // c, d (multiple results)
1076 | ),
1077 | function(a, b, c, d) {
1078 | console.log(a, b, c, d); // 6, 16, 1, 5
1079 | }]);
1080 | ```
1081 |
1082 |
1083 | `B.spread`
1084 | ```javascript
1085 | C(1, 2, 3, 4, [
1086 | B.spread(
1087 | function(a) { return a + a; }, // a
1088 |
1089 | [function(a) { return a + a; },
1090 | function(a) { return a * a; }], // b
1091 |
1092 | function(a) { return MR(a, a - a); } // c, d (multiple results)
1093 | ),
1094 | function(a, b, c, d, e) {
1095 | console.log(a, b, c, d, e); // 2, 16, 3, 0, 4
1096 | }]);
1097 |
1098 | C(1, 2, 3, 4, [
1099 | B.spread(
1100 | function(a) { return a + a; }, // a
1101 |
1102 | [function(a) { return a + a; },
1103 | function(a) { return a * a; }], // b
1104 |
1105 | function(a) { return MR(a, a - a); }, // c, d
1106 |
1107 | I, // e
1108 | I // f ** argument is undefined.
1109 | ),
1110 | function(a, b, c, d, e, f) {
1111 | console.log(a, b, c, d, e, f); // 2, 16, 3, 0, 4, undefined
1112 | }]);
1113 | ```
1114 |
1115 |
1116 | ```javascript
1117 | C(1, 5, [
1118 | B.all(
1119 | function(a, b) { return a + b; }, // a
1120 |
1121 | [function(a, b) { return a - b; },
1122 | function(a) { return a * a; }], // b
1123 |
1124 | function(a, b) { return MR(a, b); } // c, d (multiple results)
1125 | ),
1126 | C.args,
1127 | C.toArray,
1128 | function(a) {
1129 | console.log(a); // [6, 16, 1, 5]
1130 | }]);
1131 | ```
1132 |
1133 | ### 11. [this](https://github.com/marpple/abc-functional-javascript/blob/master/example/11.%20this.html)
1134 |
1135 |
1136 | #### A
1137 |
1138 | ```javascript
1139 | var r1 = A([1, 2], [
1140 | function(a, b) {
1141 | return a + b + this.c;
1142 | },
1143 | function(a) {
1144 | return a * this.c;
1145 | }
1146 | ], { c: 5 });
1147 | console.log(r1); // 40
1148 | ````
1149 |
1150 |
1151 | #### B
1152 |
1153 | ```javascript
1154 | var user1 = {
1155 | firstName: "jamm",
1156 | lastName: "Co",
1157 | getName1: B(function() {
1158 | return this.lastName + ' ' + this.firstName;
1159 | }),
1160 | getName2: B([
1161 | B.all(function() {
1162 | return this.firstName;
1163 | }, function() {
1164 | return this.lastName;
1165 | }),
1166 | function(a, b) {
1167 | return a + ' ' + b;
1168 | }
1169 | ])
1170 | };
1171 |
1172 | console.log(user1.getName1()); // Co jamm
1173 | console.log(user1.getName2()); // jamm Co
1174 |
1175 | var same_age_friends = B([
1176 | function() {
1177 | return this.friends;
1178 | },
1179 | B.filter(function(friend) { return friend.age == this.me.age })
1180 | ]);
1181 |
1182 | var r2 = same_age_friends.call({
1183 | friends: [
1184 | { id: 1, name: "a", age: 10 },
1185 | { id: 2, name: "b", age: 12 },
1186 | { id: 3, name: "c", age: 12 },
1187 | { id: 4, name: "d", age: 13 }
1188 | ],
1189 | me: { id: 5, name: "e", age: 12 }
1190 | });
1191 |
1192 | console.log(JSON.stringify(r2)); // [{"id":2,"name":"b","age":12},{"id":3,"name":"c","age":12}]
1193 | ```
1194 |
1195 |
1196 | #### C
1197 | ```javascript
1198 | var r3 = C.call({ c: 5 }, 1, 2, [
1199 | function(a, b) {
1200 | return a + b + this.c;
1201 | },
1202 | function(a) {
1203 | return a * this.c;
1204 | }
1205 | ]);
1206 | console.log(r3); // 40
1207 | ```
1208 |
1209 |
1210 | #### with jQuery
1211 | ```html
1212 |
1213 | ```
1214 | ```javascript
1215 | $(function() {
1216 | $('button').click(B([
1217 | CB(function(e, next) {
1218 | return $(this).animate({
1219 | 'margin-left': 300
1220 | }, 1000, next);
1221 | }),
1222 | function() {
1223 | $(this).text('finish');
1224 | }
1225 | ]));
1226 | });
1227 | // go --------------> finish
1228 | ```
1229 |
1230 | The `this` keyword is very important in Object Oriented Programming. Since it has to change the state of the object and refer of it.
1231 |
1232 | However, it’s difficult to handle abstract `this` keyword.
1233 |
1234 | A state to manage through ‘this’ is value. There is no need to deal with the value through 'Class'.
1235 |
1236 | Our strategy In abc.js to deal with the state :
1237 |
1238 | Deal with the value by making a new value through `function`.
1239 |
1240 |
1241 |
1242 | ### 12. [throw, ERR, CATCH](https://github.com/marpple/abc-functional-javascript/blob/master/example/13.%20CATCH.html)
1243 | ```javascript
1244 | C([
1245 | function() {
1246 | console.log(1);
1247 | },
1248 | function() {
1249 | console.log(2);
1250 | throw 2;
1251 | },
1252 | function() {
1253 | console.log(3);
1254 | },
1255 | CATCH(function(e) {
1256 | console.log(4, e);
1257 | })]);
1258 | // 1
1259 | // 2
1260 | // 4, Error: 2(…)
1261 | console.log('-------------------------');
1262 |
1263 | C([
1264 | function() {
1265 | console.log(1);
1266 | },
1267 | function() {
1268 | console.log(2);
1269 | return ERR(2);
1270 | },
1271 | function() {
1272 | console.log(3);
1273 | },
1274 | CATCH(function(e) {
1275 | console.log(4, e);
1276 | }),
1277 | function() {
1278 | console.log(5);
1279 | }]);
1280 | // 1
1281 | // 2
1282 | // 4, Error: 2(…)
1283 | // 5
1284 | console.log('-------------------------');
1285 |
1286 | C([
1287 | function() {
1288 | console.log(1);
1289 | return ERR('custom data', {
1290 | type: 1,
1291 | msg: 'hi'
1292 | });
1293 | },
1294 | CATCH(function(e) {
1295 | if (e.type == 1) {
1296 | console.log(e.msg, e);
1297 | } else {
1298 | console.log('else');
1299 | }
1300 | })]);
1301 | // 1
1302 | // hi Error: custom data(…)
1303 | console.log('-------------------------');
1304 |
1305 | C([
1306 | function() {
1307 | console.log(1);
1308 | },
1309 | function() {
1310 | console.log(2);
1311 | },
1312 | function() {
1313 | console.log(3);
1314 | },
1315 | CATCH(function(e) {
1316 | console.log(4, e);
1317 | }),
1318 | function() {
1319 | console.log(5);
1320 | }]);
1321 |
1322 | // 1
1323 | // 2
1324 | // 3
1325 | // 5
1326 | console.log('-------------------------');
1327 |
1328 | C([
1329 | function() {
1330 | console.log(1);
1331 | },
1332 | function() {
1333 | return C([
1334 | function() {
1335 | console.log(2);
1336 | },
1337 | function() {
1338 | console.log(3);
1339 | throw 3
1340 | },
1341 | function() {
1342 | console.log(4);
1343 | },
1344 | CATCH(function(e) {
1345 | console.log(5, e);
1346 | }),
1347 | function() {
1348 | console.log(6);
1349 | },
1350 | function() {
1351 | console.log(7);
1352 | return ERR(7);
1353 | }
1354 | ])
1355 | },
1356 | function() {
1357 | console.log(8);
1358 | },
1359 | CATCH(function(e) {
1360 | console.log(9, e);
1361 | return 'hi'
1362 | }),
1363 | function(a) {
1364 | console.log(10, a);
1365 | }
1366 | ]);
1367 | // 1
1368 | // 2
1369 | // 3
1370 | // 5 Error: 3(…)
1371 | // 6
1372 | // 7
1373 | // 9 Error: 7(…)
1374 | // 10 'hi'
1375 |
1376 | console.log('-------------------------');
1377 |
1378 | // for async
1379 | var go = B([
1380 | function(a) {
1381 | console.log(1);
1382 | return a;
1383 | },
1384 | CB(function(a, next) {
1385 | console.log(2);
1386 | setTimeout(function() {
1387 | next(a == 1 ? 2 : ERR(2));
1388 | }, 500)
1389 | }),
1390 | function() {
1391 | console.log(3);
1392 | },
1393 | function() {
1394 | console.log(4);
1395 | return 'complete';
1396 | },
1397 | CB(CATCH(function(e, next) {
1398 | console.log(5, e);
1399 | setTimeout(function() {
1400 | // rollback
1401 | next('fail');
1402 | }, 1000);
1403 | })),
1404 | function(a) {
1405 | console.log(a);
1406 | console.log('------------------')
1407 | }
1408 | ]);
1409 |
1410 | go(1).then(function() {
1411 | // 1
1412 | // 2
1413 | // 3
1414 | // 4
1415 | // complete
1416 |
1417 | go(2);
1418 | // 1
1419 | // 2
1420 | // 5 Error: 2(…)
1421 | // fail
1422 | });
1423 | ```
1424 |
--------------------------------------------------------------------------------
/docs/Promise 와 abcjs의 비동기 프로그래밍 비교.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Promise와 abcjs의 Pipeline 비교
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
80 |
81 |
95 |
96 |
122 |
123 |
124 |
151 |
152 |
196 |
197 |
198 |
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/docs/Promise 와 abcjs의 비동기 프로그래밍 비교.md:
--------------------------------------------------------------------------------
1 | 글 위치가 이동 되었습니다.
2 | 4. [Promise와 abcjs의 비동기 프로그래밍 비교](https://github.com/marpple/abc-functional-javascript/wiki/Promise%EC%99%80-abcjs%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EB%B9%84%EA%B5%90)
--------------------------------------------------------------------------------
/docs/Underscorejs 만들기 1 (_.map, _.each).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Underscorejs 만들기 1
6 |
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 | # Underscorejs 만들기 1
23 |
24 | ### 이전에 읽으면 좋은 글
25 | - [함수형 자바스크립트의 실용성 1](https://github.com/marpple/abc-functional-javascript/blob/master/docs/%ED%95%A8%EC%88%98%ED%98%95%20%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98%20%EC%8B%A4%EC%9A%A9%EC%84%B1%201.md)
26 | - [함수형 자바스크립트의 실용성 2](https://github.com/marpple/abc-functional-javascript/blob/master/docs/%ED%95%A8%EC%88%98%ED%98%95%20%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98%20%EC%8B%A4%EC%9A%A9%EC%84%B1%202.md)
27 |
28 | ### Underscorejs 소개
29 |
30 | Underscorejs는 작고 놀라운 함수형 자바스크립트 라이브러리다. 나는 Underscorejs의 내부 코드와 마이클 포거스의 함수형 자바스크립트라는 책을 통해 많은 것을 배웠다. 이 글에서는 Underscorejs 주요 함수들을 구현하고 그 함수를 사용할 유용한 상황을 만나면서 그 속에 담긴 컨셉과 함수형 자바스크립트의 매력을 찾아보고자 한다.
31 |
32 | 우선 왜 Underscorejs 일까. `_`를 네임스페이스로 가진다. javascript에서 `$`와 `_`는 에러 없이 사용 가능한 단 두 개의 특수 문자인데 jQuery와 Underscorejs가 모두 차지했다. 부럽다. 나도 특별한 네임스페이스를 갖고 싶어 있는 대로 키보드를 눌러봤지만 소용이 없었다. 되는게 없다. 정말 부럽다. 그래도 jQuery는 `$`를 Underscorejs는 `_`를 가질 자격이 충분하다고 생각한다. 두 라이브러리에 대한 javascript 개발자들의 존경심은 특별하다. 적어도 나는 그렇다.
33 |
34 | ### _.map, _.each
35 |
36 | Underscorejs의 `_.map, _.each` 등을 구현할 것인데 먼저 Underscorejs에 대해 좀 더 들여다보자. 우선 이 함수들은 첫 번째 인자로 약 2-4가지 정도의 타입을 받을 수 있다. 사실은 2가지라고 해야 할지 3가지라고 해야 할지 4가지라고 해야 할지 애매하다. 실용적으로 보면 4가지라고 할 수 있다. 이를 이야기하기 전에 다음을 먼저 보자.
37 |
38 |
48 |
49 | `list1`은 `Array`일까? 답부터 이야기하면 "알 수 없다." 혹은 "위 상황만 놓고는 알 수 없다."이다. javascript에서는 위와 같이 사용 가능한 객체가 `Array`만 있지 않다. 위와 같이 동작한다고 하더라도 얼마든지 다음과 같은 결과가 나올 수 있다.
50 |
51 |
55 |
56 | 그렇다면 `list1`의 정체는 무엇일까. `list1`은 일단 `Array`가 아니라고 판명 났으므로 `Array`는 아니다. 그렇다면 혹시 `arguments`는 아닐까? `arguments` 일수도 있다. 그런데 `arguments`에는 `pop`이 없는데? 그럼에도 불구하고 `arguments` 일수도 있다. 물론 아닐 수도 있다. `list1`은 무엇이었을까. 어쩌면 아래와 같았을지도 모른다.
57 |
58 |
66 |
67 | ### ArrayLike와 Underscorejs의 쿨함
68 |
69 | 다시 Underscorejs로 돌아오자. `_.each, _.map` 등에서 사용하는 객체가 4가지 정도라고 했다. 바로 `{}, [], arguments, arrayLike` 다.
70 |
71 |
88 |
89 | jQuery 객체를 사용할 때 `$('div')[0], $('div')[1]` 이렇게 사용하지만 `$('div').constructor == Array`의 결과는 `false`다. `Array`가 아니라는 것이다. d5도 당연히 `Array`가 아니다. `d3`도 `d3[1] == 2`이지만 `Array`가 아니라 즉시 실행된 익명 함수의 `arguments` 객체이다.
90 |
91 | `d1`은 완전히 `Array`가 아니다. `d2, d3, d4, d5, d6` 완전한 `Array`거나 `Array` 같은 애들이다. `Array`라고 했다가 `Array`가 아니라고 했다가 `Array` 같다고 하니 대체 뭐란 말인가.
92 |
93 | javascript는 객체를 자유롭게 만들 수 있고 객체의 `key`도 자유롭게 설정할 수 있다. 문자열과 숫자 심지어는 특수문자도 `key`로 마음껏 사용할 수 있다.
94 |
95 |
101 |
102 | `d2, d3, d4, d5, d6`은 Underscorejs의 `_.each` 등의 함수에게 그저 `arrayLike`다. 다음 `isArrayLike`의 값이 `true`면 `_.each`등은 `i++`을 이용한 `for` 문을 돌린다. `d1`은 `Array`가 아닐 것이라고 생각하고 모든 `keys`를 뽑아낸 뒤에 `keys`를 이용해 `for` 문을 돌린다.
103 |
104 |
111 |
112 | 약간 무책임해 보이지 않는가? "Array가 맞나?"라는 판단을 겨우 `.length`가 숫자 정도인지만 가지고 한다니. `_.each`등의 함수에서는 `arrayLike`가 아니더라도, 무슨 에러 조차 뱉지 않는다. 혹시 그동안 Underscorejs를 사용했던 사람이라면 혹시 배신감이 들거나 갑자기 내 코드에 에러가 있지 않을까 걱정이 되진 않는가? 걱정할 필요 없다. 아마 에러가 나지도 않았을 것이고 `try catch`를 할 필요도 없을 것이다. Underscorejs는 굉장히 쿨하다. 마치 이렇게 말하는 것 같다.
113 |
114 | "네가 잘 줄 거잖아. 개발자니까 알아서 잘 줘."
115 |
116 | Underscorejs를 보면 `try catch`가 단 한번 나온다. 심지어 그 `try catch`도 `eval`과 비슷한 코드를 돌리는 곳에서 딱 한 번이다. 나는 사실 유명한 라이브러리들 중에 이토록 쿨한 라이브러리를 별로 본 적이 없었다. 심지어 Underscorejs는 다름 아닌 Data를 주로 다루는 라이브러리다. 또 Data를 다루는 많은 유명 라이브러리(Backbone, ORM 등)안에서 많이 사용되어지고 있다. 근데 어떻게 이렇게 데이터 형에 대해 관대하고 쿨하다니. 아무거나 막 받고 에러를 처리해주지도 않고 그냥 돌아간다. 죽으라는거다 그냥.
117 |
118 | 근데 또 잘 죽지도 않는다. 어떻게 그럴까. 데이터 형을 체크하지 않아도 데이터를 너무나 잘 다루고 있기 때문이다. 자신들이 사용하고 있는 Native Helper나 자신이 만든 함수의 동작에 대해 정확히 알고 너무나 합을 잘 맞추고 있는 것이다.
119 |
120 |
131 |
132 | 나는 이렇게 데이터형에 대해 관대하고 쿨한 Underscorejs를 보며 많은 것을 느꼈다. 그동안 내가 얼마나 지레 겁먹고 에러 처리를 했는지. 혹은 `if`문을 추가했었는지. 사실은 그것이 얼마나 데이터나 주어진 API들에 대한 무지에서 비롯되었던 것인지 느낄 수 있었다. Underscorejs는 마냥 단순하게 무책임하지 않는다. 개발자가 '목적에 의해' 웬만해선 절대 실수하지 않을 영역에 대해서는 그냥 쿨해버린다. 그렇지 않은 곳은 다형성을 잘 지원하는 것 이상의 감각으로 에러를 내지 않고 있다. 언어에 대한 감각, javascript 데이터형에 대한 이해, Native Helpers, Methods에 대한 높은 이해를 바탕으로 짧고 간결하고 풍성한 이 라이브러리를 만든 그들을 존경하지 않을 수 없다. 그렇기에 그들은 아마도 Underscorejs, CoffeeScript, Backbone 등을 만들며 javascript 진영에 큰 영향력을 끼쳤을 것이다.
133 |
134 | ### _.map 만들기
135 |
136 | 이야기가 길었지만 Underscorejs에 담긴 컨셉과 생각을 이해하려면 꼭 필요한 검토였다고 생각한다. 자 이제 `_.map`을 구현해보자. `_.map`을 구현하기에 앞서 얘기해두자면 Underscorejs처럼 낙후한 브라우저까지 커버하지는 않겠다. 본 글은 함수형 자바스크립트에 대해 알아보기 위한 글이고 예제이므로 이해를 위해 최신 Native Helpers를 사용하기도 하고 안 하기도 하겠고 Chrome과 Nodejs에서만 테스트를 하겠다.
137 |
138 |
147 |
148 | 위 코드가 앞서 만들었던 `_.map`이다. Underscorejs는 _.map의 첫 번째 인자로 `{}, [], arguments, arrayLike` 등을 받는다고 했다. 우선 `.length` 검사를 통해 `i++`에 의존하여 `for`를 돌릴지 여부를 결정하고 아니라면 `for in` 문을 활용해보자.
149 |
150 |
167 |
168 | 제법 그럴싸해졌다. Underscorejs의 `_.map`은 세 번째 인자로 `iteratee`에서 사용할 `this` 도 전달할 수 있지만 우리는 그렇게 하지 않겠다. 코드는 조금만 손대면 되지만 설명은 하지 않겠다. 좀 더 함수적인 아이디어에만 집중하자.
169 |
170 |
198 |
199 | 이제 `_.map` 하나가 완성되었다. 이 `_.map` 하나 말고는 아직 가진 것이 아무것도 없다. `_.map`이 그럴싸해졌으니 '쓸모없는' 함수 `_.idenetity`등과 함께 사용하여 재밌는 함수를 만들어보자.
200 |
201 |
224 |
225 | `_.identity, _.values, _.keys` 모두 Underscorejs에 있는 함수지만 Underscorejs와는 다른 순서로 다른 코드로 Underscorejs를 만들고 있다. `_.a2`는 아마 없을 것 같다. 재미있는 함수다.
226 |
227 | ### _.each 만들기
228 |
229 | 몇 개 함수가 생겼으니 `_.each`도 쉽게 만들 수 있을 것 같다. 하지만 내부에서 `_.map`을 쓰진 않겠다. 왜냐면 `_.each`와 `_.map`은 로직이 완전히 다른 함수이기 때문이다. `_.map`을 쓰진 않지만 `_.map`으로 만든 `_.keys`를 사용하여 쉽게 다형성을 만들었다.
230 |
231 |
256 |
257 | `bCurrentKey`는 앞서 보였던 `badd` 등과 동일한 패턴이다. Underscorejs의 `_.map, _.some, _.mapObject, _.every` 등은 위의 `bCurrentKey` 같은 방식으로 구현되어 있지는 않고 아래처럼 되어있다.
258 |
259 |
265 |
266 | 차이를 발견했는가? 우리가 만든 `_.map`은 `keys`가 있는지 없는지를 한 번만 비교하고서는 그 이후는 무조건 동일한 함수 실행으로 인해 결과만 뱉기에 삼항 연산자가 한 번만 돌고 그 이후는 결과만 만든다. Underscorejs의 구현은 삼항 연산자가 `length` 만큼 동작할 것이다.
267 |
268 | ### 함수형 패러다임
269 |
270 | 함수형 패러다임을 잘 활용하면 다형성을 지키면서도 성능을 놓치지 않는 아이디어를 만들 수 있다. 위 상황만 놓고 보면 무엇이 더 성능이 좋을지 잘 모르겠다. 하지만 논리적으로 놓고 보면 두 코드는 큰 차이를 가지고 있다. 한 번만 비교하느냐 100번이고 1,000번이고 비교하느냐의 차이를 가지고 있다. 위와 같이 간단한 연산에서는 두 코드의 패턴이 성능 차이를 많이 내진 않을 것이다. 하지만 나는 저 코드 패턴 자체를 소개하고 싶다. 만일 조건식 안에서만 또 loop를 1,000번 돌아야 한다던지, 데이터베이스에 다녀와야 한다던지 한다면 두 패턴은 완전히 다른 성능을 낼 것이다.
271 |
272 | 아무것도 없는 상태로 돌아가 `_.map, _.identity, _.values, _.a2, _.keys, _.each` 순으로 구현했다. 모든 함수는 서로를 사용할지라도 의존성이 없다. 인자와 결과 외에는 서로의 내부에서 무슨 일을 하는지에 대해 관심이 없다. 인자와 결과만 같다면 다른 함수로 대체돼도 아무 상관이 없다. 서로의 데이터를 변경하고 있지 않다. 서로가 사용하는 데이터가 어떻게 생겼는지 관심도 없다. 모든 함수가 같은 인자를 주면 항상 같은 값을 뱉는 순수 함수로만 이루어졌다. 동작 외적인 '준비를 위한 코드'도 없다. 모두 선언 즉시 사용이 가능한 함수다. 클래스도 없고 상속도 없다. 설정값도 없다. 새로운 객체가 initialize 돼야 하거나 되어있지 않다. 사용을 위한 별도의 loading이나 타이밍이 필요하지 않다. 기록되는 상태가 없어 부수 효과도 메모리 릭도 있을 수 없다. 상황과 상태의 의존하지 않으므로 전역에 선언되어도 상관이 없다. 앞으로 어떻게 발전할지에 대한 준비도 설계도 딱히 없다. 그냥 지금 만들고 싶은 함수와 필요한 함수들만 만들었다. 함수형 자바스크립트는 어쩌면 극 실용주의인지도 모른다. Underscorejs의 쿨함처럼.
273 |
274 | ### 한가지 더
275 | `bCurrentKey`가 실행되는 환경에서 `keys`가 없다면 `function(i) { return keys[i]; }` 이 익명함수는 정의되지 않으므로 클로저가 되지 않는다.
276 |
277 | --------------
278 |
279 | - [함수형 자바스크립트의 실용성 1](https://github.com/marpple/abc-functional-javascript/blob/master/docs/%ED%95%A8%EC%88%98%ED%98%95%20%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98%20%EC%8B%A4%EC%9A%A9%EC%84%B1%201.md)
280 | - [함수형 자바스크립트의 실용성 2](https://github.com/marpple/abc-functional-javascript/blob/master/docs/%ED%95%A8%EC%88%98%ED%98%95%20%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98%20%EC%8B%A4%EC%9A%A9%EC%84%B1%202.md)
281 |
282 | [다른 글 모두 보기] (https://github.com/marpple/abc-functional-javascript/tree/master/docs)
283 |
284 |
285 |
--------------------------------------------------------------------------------
/docs/Underscorejs 만들기 1 (_.map, _.each).md:
--------------------------------------------------------------------------------
1 | 글 위치가 이동 되었습니다.
2 | 3. [Underscorejs 만들기 1](https://github.com/marpple/abc-functional-javascript/wiki/Underscorejs-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-(_.map,-_.each))
--------------------------------------------------------------------------------
/docs/함수형 자바스크립트의 실용성 1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 함수형 자바스크립트의 실용성 1
6 |
7 |
8 |
9 |
10 | '함수형 프로그래밍 그거 먹는건가요?'
11 |
12 | 커링, 부분 실행등의 함수형 자바스크립트 관련 예제들을 보거나 대표적인 함수형 자바스크립트 라이브러리인 Underscorejs를 보면서도 each, map, reduce 등이 어떻게 동작하는 함수인지는 눈치챘지만 감탄스럽다기보단 그냥 '아 그렇구나' 혹은 다음과 같은 느낌이었다. '어떻게 돌아가는지는 알겠는데 저걸 어떤 상황에서 쓰는거지?'
13 |
14 |
22 |
23 | 구글에서 함수형 자바스크립트를 검색해보면 위와 같은 예제를 많이 만나게 된다. 커링 혹은 부분 실행과 관련된 코드들이다. 생소할뿐 조금 들여다보면 어렵지는 않다. 결론부터 얘기하면 전혀 실용적이지 않은 것 같은 badd 같은 패턴이 사실은 굉장히 많이 사용된다. badd는 함수를 값으로 다루는 함수다. badd의 리턴 값은 함수다. 그러므로 badd(10)의 결과가 함수다. 결과가 함수이므로 (5) 실행을 할 수 있다. 두개의 함수가 한번씩 실행되었고 실행 결과는 15 다.
24 |
25 |
30 |
31 | badd(5)를 실행하여 add5라고 이름을 지어주었다. 그리고는 3도 더하고 4도 더했다. 이 예제들은 간단하지만 많은 이야기를 담고 있다. 그렇지만 지금은 짧게 얘기하겠다. 자바스크립트에서는 다음처럼 함수를 값으로 다룰 수 있고 함수안에서 정의된 값은 리턴할 수 있다.
32 |
33 |
39 |
40 | 다시 돌아와 badd 예제를 보자.
41 |
42 |
58 |
59 | 함수는 값을 리턴할 수 있고 함수는 값이 될 수 있다. badd는 함수를 정의하면서 리턴했다. badd가 리턴한 익명함수는 클로저가 되었다. 클로저가 무엇인지 정확히 설명하려면 많은 예제와 설명이 필요하다. 지금 당장은 정확한 클로저에 대해 모르더라도 a가 한번은 5이고 한번은 3이여서 8, 9, 6, 7 의 결과가 나오는 것은 충분히 유추할 수 있다. 지금은 그냥 함수 내부에 정의된적 없는 a 가 부모 스코프에 있어 그 값을 참조하여 결과를 만들었다고만 생각하자.
60 |
61 | 위 예제에서는 badd라는 함수안에서 a는 익명함수가 리턴되기전과 익명함수내부에서 값을 변경하고 있지 않기 때문에 리턴된 익명함수에서의 a는 항상 동일한 값을 가지고 있게 된다. 모든 상황에서 클로저가 된 함수가 가르키고 있는 변수의 값이 불변하지는 않는다. 클로저에 대한 자세한 설명은 나중에 하겠다. 지금은 그저 위와 같은 코드가 값으로서의 함수와 클로저를 이용한 함수형 자바스크립트 스타일 중 하나라는 것을 기억해두고 다음을 보자.
62 |
63 |
100 |
101 | 위 코드는 실무에서 자주 다뤄질법한 익숙한 예제다.
102 | users 중에 age 가 30 이하인 user 만 모아서 몇명인지를 출력하고 그들의 나이만 다시 모아서 출력했다. 그 밑에는 나이가 30 이상인 user 가 몇명인지와 이름들을 출력하고 있다. 위 코드를 리팩토링 하면서 함수형 자바스크립트의 실용성을 알아보자.
103 |
104 | 중복되는 부분을 찾아보자. 첫번째로는 users 를 돌면서 특정 조건의 user를 새로운 array에 담고 있다. if 문의 조건절 부분을 제외하고는 모두 동일한 코드를 가지고 있다. 한번은 .age < 30 한번은 .age >= 30 만 다를뿐 다른 부분은 모두 동일하다. 어떻게 중복을 제거해야할까. 30 은 변수로 바꿔볼 수 있겠다 싶지만 .age, <, >= 등은 쉽지 않아 보인다. 어떻게 하면 가능할까? 함수를 활용하면 가능하다. 다음을 보자.
105 |
106 |
122 |
123 | 기존의 코드를 활용해 filter 함수를 만들었다. 실행해보기 전에 filter 함수를 들여다보면 list 를 받아 loop 를 돌면서 if 문의 조건절에서 인자로 받았던 predicate 함수를 실행한다. predicate 함수의 결과가 true 면 new_list.push 가 실행될 것이다. new_list.push 실행될지의 여부를 predicate 함수에게 위임했다. 마지막엔 new_list 를 리턴했다. 이름을 new 라고 붙였는데 이는 함수형 프로그래밍에서 굉장히 상징적이다. 이전 값의 상태를 변경하지 않고 새로운 값을 만들어 리턴하는것은 함수형 프로그래밍적 관점에서 매우 중요한 방식이다.
124 |
125 |
144 |
145 | 꽤 코드가 짧아졌다. 그리고 filter 라는 재사용 가능한 함수가 생겼다. 이번에도 기존 코드를 활용해 map 이라는 함수를 만들어보자.
146 |
147 |
163 |
164 | 이번에도 로직은 기존과 같다. 단지 무엇을 new_list에 push 할지에 대해 iteratee 함수에게 위임했다. 바로 실행해보자.
165 |
166 |
179 |
180 | 위 상황에서는 함수의 리턴 값을 바로 다시 함수의 인자로 사용하면 변수할당을 줄일 수 있다.
181 |
182 |
197 |
198 | 작은 함수를 하나 더 만들면 변수 할당을 모두 없앨 수 있다.
199 |
200 |
206 |
207 |
218 |
219 | 만들어진 모든 코드를 모아 이전 코드와 확인해보자.
220 |
221 |
275 |
276 | 마지막으로 badd와 비슷한 패턴의 함수를 하나 만들어 코드를 더욱 줄여보자.
277 |
278 |
291 |
292 |
304 |
305 | badd 와 같은 패턴의 함수도 위와 같이 실용적이었다. 함수형 프로그래밍의 실용성에 대해 다뤄보았다. '함수형 자바스크립트' 꽤 실용적일 것 같은 예감이 들지 않는가?
306 |
307 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/docs/함수형 자바스크립트의 실용성 1.md:
--------------------------------------------------------------------------------
1 | 글 위치가 이동 되었습니다.
2 | 1. [함수형 자바스크립트의 실용성 1](https://github.com/marpple/abc-functional-javascript/wiki/%ED%95%A8%EC%88%98%ED%98%95-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%8B%A4%EC%9A%A9%EC%84%B1-1)
--------------------------------------------------------------------------------
/docs/함수형 자바스크립트의 실용성 2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 함수형 자바스크립트의 실용성 2
6 |
23 |
24 |
25 |
26 | [함수형 자바스크립트의 실용성 1] (https://github.com/marpple/abc-functional-javascript/tree/master/docs)
27 |
28 | --------------
29 |
30 | # 함수형 자바스크립트의 실용성 2
31 |
32 | ## 값 대신 함수
33 |
34 |
45 |
46 | `id`를 통해 한명의 `user`를 찾고 싶다.
47 |
48 |
52 |
53 | 만들었던 `filter`를 이용해 찾을 수 있다. 하지만 `filter` 함수는 `list.length` 만큼 `predicate`가 실행되기 때문에 효율적이지 못하다. 다음과 같은 코드가 효율적일 것이다.
54 |
55 |
65 |
66 | 위 로직을 함수로 만들면 재사용이 가능해진다.
67 |
68 |
76 |
77 | 이번엔 이름으로도 찾고 싶다.
78 |
79 |
87 |
88 | 나이로 찾는 함수도 만들어 두고 싶어진다.
89 |
90 |
98 |
99 | 함수로 만들어 `for` 와 `if` 등의 로직을 숨기고 코드도 줄였지만 뭔가 아쉽다. `find_by_id, find_by_name, find_by_age` 사이에 중복이 있기 때문이다. 결론 부터 얘기하면 위 함수는 함수형적이지 않다. 중복을 제거한 다음 코드도 역시 한계가 있다.
100 |
101 |
110 |
111 | 코드가 거의 3분의 1로 줄었다. 아니 데이터가 `users, posts, comments, products` 등 앞으로 데이터의 key 값이 무엇이든지 간에 찾을 수 있는 함수이므로 훨씬 많은 경우를 대응할 수 있는 함수가 되었다. 하지만 다음과 같은 상황은 지원하지 못한다. 하지만 값을 메소드를 통해 얻어야하거나 두가지 이상의 조건이 필요하다면, 혹은 `==` 이 아니라면.
112 |
113 |
143 |
144 | getAge() 로 값을 얻어야하기 때문에 `find_by` 함수는 위 상황을 대응 할 수 없다. 또한 이름에 P가 포함되었다거나 나이가 32이면서 이름이 JM인 `user`를 찾고 싶다거나 하는것도 불가능하다. 나이가 30세 미만인 사람을 찾는 것도 `find_by`로는 할 수 없다. 보다 함수적인 프로그래밍을 해보자. `filter, map`과 같은 고차 함수로 만들면 거의 모든 상황에 대응 가능한 `find` 함수가 될 수 있다.
145 |
146 |
161 |
162 |
163 | 인자로 `string, number` 대신 `function`을 받도록 한 이 작은 차이는 실용적인 관점에서 비교할 수 없는 차이를 만들었다. `filter`와 `map` 역시 마찬가지이다. 단순히 함수형 자바스크립트의 실용성1의 예제 상황만을 줄인 코드가 아니다. `users`나 key/value 객체만을 위한 함수가 아닌 것이다. 함수형 프로그래밍은 다형성을 높이는 기법을 많이 사용하고 이는 실용성을 매우 높인다. 다형성을 높이면 안정성이 떨어질까? 함수형 프로그래밍은 상태를 변경하지 않음으로 안정성을 높인다. 또한 개발자는 데이터를 고차함수에게 넘길때 그 데이터의 형을 이미 알고 있다. 그리고 `predicate` 등의 보조 함수 역시 개발자가 넘기기 때문에 안정성을 보장하는데 문제가 없다.
164 |
165 |
173 |
174 | `find`로 다시 돌아와 `badd` 같은 함수를 만들어 코드를 더 줄여보자.
175 |
176 |
187 |
188 | `bmatch`의 실행결과는 함수이므로 `filter`나 `map`과도 조합이 가능하다.
189 |
190 |
196 |
197 | `bmatch1` 을 더 발전시켜보자.
198 |
199 |
226 |
227 | `bmatch1`을 발전시키면서 유용한 함수인 `match`와 `object`도 만들어졌다. 함수형 자바스크립트는 적은 기능을 하는 함수를 조합하거나 작은 수정을 통해 코드를 발전시켜나간다. `find` 를 조금만 고치면 값 비교만 하는 `Array.prototype.indexOf` 보다 풍성한 `find_index`를 만들 수 있다. `find_index` 역시 고차함수 이므로 다른 함수와 동일하게 조합 가능하다.
228 |
229 |
241 |
242 | ## 고차함수
243 |
244 | 앞서 구현했던 `map, filter, find, find_index, bvalue, bmatch` 같은 함수들은 고차함수다. 고차 함수란 함수를 값으로 다루거나 함수를 인자로 받아 사용하는 함수를 말한다. `map, filter, find, find_index` 모두 Underscorejs에 있는 함수다. 이름은 다르지만 `match, bmatch`도 있다. Underscorejs는 GitHub 별이 19,235개나 되는 유명한 함수형 자바스크립트 라이브러리다. `map, filter, find, find_index` 함수를 Underscorejs의 `_.map, _.filter, _.find, _.findIndex`에 좀더 가깝게 고쳐보면 다음과 같다.
245 |
246 |
272 |
273 | `iteratee`와 `predicate`함수에게 넘기는 인자가 더 많아졌다. `iteratee`나 `predicate`가 더 다양한 일을 할수 있게 하기 위해서다. 이제 `_.filter` 함수의 `predicate`에게 두번째 인자로 i가 넘어오므로 다음과 같은 함수조합이 가능해졌다.
274 |
275 |
286 |
287 | ## _.identity = function(v) { return v; } 이건 어디다 쓰는거지?
288 |
289 | 이상한 함수 하나를 소개한다. Underscorejs에 있는 함수이기도 하다.
290 |
291 |
296 |
297 | 함수를 정의하고 실행해보았다. 받은 인자를 그냥 그대로 뱉는 함수다. 나는 이미 `a`가 10인줄 알고 있는데 _.identity 같은 아무런 기능이 없는 함수는 대체 언제 사용해야하는걸까? 다음을 보자.
298 |
299 |
303 |
304 | `_.filter`를 `_.identity` 사용했더니 긍정적인 값만 남았다. 이렇게 놓고보니 생각보다 굉장히 실용적이다. `_.identity`를 다른 고차 함수와 조합하면 이런 함수들도 만들 수 있다.
305 |
306 |
319 |
320 | `_.some` 은 배열에 들어 있는 값 중에 하나라도 긍정적인 값이 있으면 `true`를, 하나도 없다면 `false`를 리턴한다. `_.every` 는 모두 긍정적인 값이어야 true를 뱉는다. 그런데 사실 `_.every`는 뭔가 아쉽다. 정말 쓸모 없어 보이는 함수를 두개만 더 만들어서 로직을 개선해보자.
321 |
322 |
330 |
331 | !를 써도 되는데 not이 왜 필요할까 싶다. === 로 비교하면 되는데 beq 역시 왜 필요할까 싶다. 굳이 함수로 만들었어야 할까. 다음을 보자.
332 |
333 |
346 |
347 | 우선 `_.every`는 `not`이 있어 `_.filter` 를 사용했을때보다 로직이 개선되었다. 좀더 함수를 쪼개보자. 최대한 한가지일만 하겠금 말이다.
348 |
349 |
368 |
369 | Underscorejs의 _.compose와 함께 사용해보자.
370 | _.compose는 맨 오른쪽의 함수를 실행한 결과를 바로 왼쪽의 함수에게 전달하고 그 함수의 실행결과는 또 왼쪽의 함수에게 전달되도록 하는 고차함수다.
371 |
372 |
389 |
390 |
405 |
406 | _.compose와 같은 함수합성기법을 자주 사용하면서 코딩을 하면 코드를 짧게 할 수 있고, 인자와 변수가 보이지 않게 하여 함수들의 순수성을 높일 수 있다. 부수효과를 최소화하게 된다. 순수함수들만 사용한다면 더더욱 그렇다. 나중에 읽기 쉽다. 오른쪽에서 왼쪽 방향으로 하나씩 무슨일을 하는지 인자와 결과만을 생각하면서 보면 되기 때문이다.
407 |
408 | --------------
409 |
410 | [함수형 자바스크립트의 실용성 1] (https://github.com/marpple/abc-functional-javascript/tree/master/docs)
411 |
412 | [다른 글 모두 보기] (https://github.com/marpple/abc-functional-javascript/tree/master/docs)
413 |
414 |
415 |
416 |
417 |
--------------------------------------------------------------------------------
/docs/함수형 자바스크립트의 실용성 2.md:
--------------------------------------------------------------------------------
1 | 글 위치가 이동 되었습니다.
2 | 2. [함수형 자바스크립트의 실용성 2](https://github.com/marpple/abc-functional-javascript/wiki/%ED%95%A8%EC%88%98%ED%98%95-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%8B%A4%EC%9A%A9%EC%84%B1-2)
--------------------------------------------------------------------------------
/etc/docs/promise_and_abc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Promise와 abcjs의 Pipeline 비교
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
80 |
81 |
95 |
96 |
122 |
123 |
124 |
151 |
152 |
196 |
197 |
198 |
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/etc/docs/promise_and_abc.md:
--------------------------------------------------------------------------------
1 | # Promise와 abcjs 비교
2 |
3 | Promise는 비동기 상황에서 동기적으로 코드를 작성할 수 있게 하는 해결책 중 하나이며 ES6에 채택되었다. Promise는 "값이 만들어지기로 약속된 Promise 객체를 즉시 리턴하는 함수"들을 만들고 이것들을 내부에서 순차적으로 실행하며 비동기 상황을 제어한다. 결과 값은 아니지만 Promise 객체를 즉시 리턴한다는 특징 덕분에 비동기 상황을 if, else, for 등으로 어느 정도 제어할 수 있게 된다. 그리고 try catch와 유사한 에러 핸들링이 가능해진다. 콜백 지옥 해결을 넘어 동기적인 코드 작성을 할 수 있게 하고 ES7의 async await 키워드의 사용을 가능하게 한다. 이러한 이유로 Promise는 더 많은 javascript 개발자들에게 선택되어지고 있다.
4 |
5 | Promise는 충분한 해결책이지만 다음과 같은 작은 단점들이 있다고 생각한다.
6 |
7 | 1. 반드시 비동기가 일어난다.
8 | 2. Promise로 감싸진 의미 없는 익명 함수가 많아진다.
9 | 3. 익명 함수에서 인자를 2개 이상 받을 수 없어 일반 함수나 타 라이브러리 등과 조합하기 어렵다.
10 | 4. resolve가 값을 1개만 전달하므로 결과가 2개 이상인 콜백 패턴의 함수와 조합하기 어렵다.
11 | 5. Promise 객체를 리턴하는 함수를 사용하는건 쉽지만 직접 만드는 것은 약간 어렵다.
12 | 6. 콜백 함수를 받는 고차 함수를 Promise로 제어하려면 제법 많은 Promise 관련 코드가 필요하다.
13 |
14 | 다음 예제들은 abcjs의 Pipeline과 Promise와의 차이점을 보여준다.
15 |
16 | ```javascript
17 |
18 | function add(a, b, next) {
19 | setTimeout(function() {
20 | next(a + b);
21 | }, 1000);
22 | }
23 |
24 | function sub(a, b, next) {
25 | setTimeout(function() {
26 | next(a - b);
27 | }, 1000);
28 | }
29 |
30 | function mul(a, b, next) {
31 | setTimeout(function() {
32 | next(a * b);
33 | }, 1000);
34 | }
35 |
36 | function log(msg, next) {
37 | setTimeout(function() {
38 | console.log(msg);
39 | next(msg);
40 | }, 1000);
41 | }
42 |
43 | /* Promise 1 */
44 | new Promise(
45 | function(resolve) {
46 | add(5, 10, resolve);
47 | })
48 | .then(function(result) {
49 | return new Promise(function(resolve) {
50 | sub(result, 10, resolve);
51 | });
52 | })
53 | .then(function(result) {
54 | return new Promise(function(resolve) {
55 | mul(result, 1, resolve);
56 | });
57 | })
58 | .then(function(result) {
59 | return new Promise(function(resolve) {
60 | log(result, resolve);
61 | });
62 | });
63 | // 5
64 |
65 | /* abcjs 1 */
66 | C(5, 10, CB(
67 | function(a, b, next) {
68 | add(a, b, next);
69 | },
70 | function(result, next) {
71 | sub(result, 10, next);
72 | },
73 | function(result, next) {
74 | mul(result, 10, next);
75 | },
76 | function(result, next) {
77 | log(result, next);
78 | }));
79 | // 50
80 |
81 | ```
82 |
83 | Promise는 다음 .then에게 결과를 넘겨주기 위해 새로운 Promise 객체를 생성해야 한다. 이와 달리 abcjs의 Pipeline은 next를 받을 함수들에 CB를 감싸주기만 하면 된다.
84 |
85 | Promise는 wrapper 역할을 하고 있는 익명 함수가 반드시 하나의 인자만 받을 수 있다. resolve 함수에게도 여러개의 인자를 넘길 수 없다. 문제점이라기보단 그렇게 의도된 것이다. Promise의 최종 값은 마지막 익명 함수의 return으로 결정 될 수 있기 때문에 일관성을 위해 그렇게 선택했을 것이다.
86 |
87 | ```javascript
88 |
89 | /* not working */
90 | new Promise(
91 | function(resolve) {
92 | add(5, 10, function(result) {
93 | resolve(result, 10);
94 | });
95 | })
96 | .then(function(a, b) { // b is undefined
97 | return new Promise(function(resolve) {
98 | sub(a, b, resolve);
99 | });
100 | });
101 |
102 | // ES6 Promise에는 없지만 bluebirdjs에서는 array로 값을 넘긴 후 spread로 인자로 나눠 받을 수 있다.
103 | // resolve([result, 10]); -> .spread(function(a, b) {})
104 |
105 | ```
106 |
107 | abcjs에서 wrapper 역할을 하는 함수는 여러 개의 인자를 받을 수 있으며 next는 마지막 인자로 들어온다. 그 역시 일반적인 콜백 패턴의 함수가 된다. 이 덕분에 wrapper 함수를 선언하는 부분에서 일반적인 함수 선언이나 함수 참조가 가능해진다. 이와 같은 특징과 대부분의 콜백 패턴이 콜백 함수를 마지막 인자로 받는다는 점등을 활용하면 next, _.partial, abcjs의 B 등을 통해 다음과 같은 코드를 작성할 수 있다.
108 |
109 | ```javascript
110 | /* abcjs 2 */
111 | C(5, 10, CB(
112 | add,
113 | function(result, next) {
114 | sub(result, 10, function(result) {
115 | next(result, 20); // multiple results
116 | });
117 | },
118 | mul,
119 | log));
120 | // 100
121 |
122 | /* abcjs 3 */
123 | C(5, 10, CB(
124 | add,
125 | _.partial(sub, _, 10),
126 | _.partial(mul, 30),
127 | log));
128 | // 150
129 |
130 | /* abcjs 4 */
131 | C(5, 10, CB(
132 | add, B(X, 10, sub), B(40, mul), log));
133 | // 200
134 | ```
135 |
136 | 물론 Promise를 이용하더라도 아래의 promisify 같은 함수 구현을 통해 비슷한 코드셋을 만들 수 있다. 하지만 then까지 제거되진 않는다. then을 내부에서 풀어주는 함수를 만들면 then도 숨길 수 있겠지만 abcjs와 같은 코드모양이 되어야한다. abcjs와 같은 모양이 되면 Promise처럼 모나드 패턴으로 비동기를 제어하지 않아도 되므로 함수마다 Promise 객체를 품도록 구현 되는건 오버스펙이 되는게 아닐까 생각된다.
137 |
138 | ```javascript
139 |
140 | function promisify(func) {
141 | return function() {
142 | var args = _.toArray(arguments);
143 | var self = this;
144 | return new Promise(function(resolve) {
145 | func.apply(self, args.concat(resolve));
146 | });
147 | }
148 | }
149 |
150 | /* Promise 2 */
151 | promisify(add)(5, 10)
152 | .then(_.partial(promisify(sub), _, 10))
153 | .then(_.partial(promisify(mul), 50))
154 | .then(promisify(log));
155 | // 250
156 |
157 | /* Promise 3 */
158 | promisify(add)(5, 10)
159 | .then(B(X, 10, promisify(sub)))
160 | .then(B(60, promisify(mul)))
161 | .then(promisify(log));
162 | // 300
163 |
164 | ```
165 |
166 | 코드를 줄이는게 목적이라면 Promise도 promisify와 같은 일을 미리 해두면 되지 않겠는가라고 생각할 수 있다. 하지만 모든 콜백 함수를 각 함수에 맞게 미리 promisify 해두는 일은 쉽지 않으며 기존 레퍼런스를 덮어버릴 경우 callback 함수를 동적(arguments.length[arguments.length-1])으로 받는 함수가 있다면 그 함수의 라이브러리 내부에서 오류가 나는 문제가 생길 수 있다.
167 |
168 | 반면에 abcjs는 함수를 감싼 새로운 함수를 뱉지 않는다. 함수에게 callback 패턴의 함수라는 단서만 남겨 놓는다. 함수 자체는 바꾸지 않기 때문에 다른 변수에 정의해야할 필요가 없다. 기존의 콜백 방식 그대로도 사용 할 수 있기에 원래의 사용처에서 오류가 날 이유도 없다. abcjs도 미리 CB로 감싸둔다면 이후 Pipeline 사용시 더욱 간단하게 사용 가능하다.
169 |
170 | ```javascript
171 |
172 | /* Promise 4 (with promisify) */
173 | var add2 = promisify(add);
174 | var sub2 = promisify(sub);
175 | var mul2 = promisify(mul);
176 | var log2 = promisify(log);
177 |
178 | add2(5, 10).then(B(X, 10, sub2)).then(B(X, 70, mul2)).then(log2);
179 | // 350
180 |
181 | /* abcjs 5 */
182 | CB(add, sub, mul);
183 | C(5, 10, [add, B(X, 10, sub), B(80, mul), log]);
184 | // 400
185 |
186 | /* abcjs 6 (with promisify) */
187 | C(5, 10, [add2, B(X, 10, sub2), B(90, mul2), log2]);
188 | // 500
189 |
190 | /* abcjs 7 (with Promise)*/
191 | C([
192 | function() {
193 | return new Promise(function(resolve) {
194 | add(5, 10, resolve);
195 | });
196 | },
197 | function(result) {
198 | return new Promise(function(resolve) {
199 | sub(result, 10, resolve);
200 | });
201 | },
202 | function(result) {
203 | return new Promise(function(resolve) {
204 | mul(result, 100, resolve);
205 | });
206 | },
207 | function(result) {
208 | return new Promise(function(resolve) {
209 | log(result, resolve);
210 | });
211 | }]);
212 | // 550
213 |
214 | ```
215 |
216 | abcjs는 Promise에 대한 의존성이 없지만 Promise와 함께 사용이 가능하다. Pipeline 내부의 함수 결과가 Promise나 jQuery Deferred Object 와 같은 then 메소드를 가진 객체일 경우 결과를 꺼내 Pipeline의 다음 함수에게 전달한다.
217 |
218 | abcjs의 Pipeline은 Promise와 달리 함수들을 실행하다 비동기 상황을 만났을때만 then 메소드를 가진 약식 Promise 객체를 생성하여 즉시 리턴하고 재귀를 통해서만 비동기를 제어한다. 이 약식 Promise는 중첩 Pipeline 등을 위해 사용된다. ES6 이상이거나 Promise 생성자가 있을 경우에는 약식 Promise가 아닌 정식 Promise를 리턴하기 때문에 Promise나 ES7의 async await 등과도 연동 된다.
219 |
220 | 잠깐 쉬어가는 의미로 아래 코드를 보자. 내가 실무에서 사용하지는 않지만 재밌는 코드가 있다.
221 | B로 감싸주기만 하면 add, sub, mul 모두 비동기 함수임에도 아래와 같은 코드가 동작한다.
222 |
223 | ```javascript
224 | /* abcjs 8 (bfy) */
225 | var badd = B(add);
226 | var bsub10 = B(X, 10, sub);
227 | var bmul110 = B(110, mul);
228 | var blog = B(log);
229 |
230 | blog(bmul110(bsub10(badd(5, 10))));
231 | // 550
232 | ```
233 |
234 | 물론 Promise를 이용해서도 B, C, CB와 같은 함수들을 모두 구현할 수 있다. 그렇지만 abcjs는 Promise 없이 구현되었으며 재귀만으로 비동기를 제어한다. Promise는 현재 모든 브라우저에서 동작하지 않아 bluebirdjs 등의 라이브러리가 필요하다. ES6의 Promise보다 bluebirdjs가 기능적으로 좀더 풍성하여 ES6이 지원되는 NodeJS 환경에서도 bluebirdjs를 많이 사용한다. abcjs는 bluebirdjs 보다 좀 더 많은 비동기 제어 기능을 가지고 있다. 비동기 관련 기능 외에도 HTML 템플릿 엔진, 깊은 값 변경 등의 다양한 기능이 있다. 다음은 bluebirdjs와 abcjs의 비동기 제어와 관련된 API 비교 표다.
235 |
236 |
237 | | | bluebirdjs | abcjs |
238 | |----------------------|--------------------------------------------------------------|--------------------------------------------------------------------------------|
239 | | Multiple Results | all, spread | MR, spread, all, auto multiple results(callback) |
240 | | Functional, Control Statement | race, props, any, some, map, reduce, filter, each, mapSeries | each, map, reduce, filter, reject, find, findIndex, findKey, some, every, uniq, IF, ELSEIF, ELSE |
241 | | Error Handling | throw, catch, reject | throw, CATCH, ERR |
242 | | etc | tap, delay, promisify, promisifyAll ... | tap, delay, bfy, CB, async jade template, async string template ... |
243 | | Line | 5,598 | 961 |
244 |
245 | abcjs는 실행될 모든 함수를 미리 받아 재귀만으로 함수들을 제어한다. 이 특징을 활용하여 abcjs의 모든 함수들은 동기와 비동기 지원 함수를 별도로 나누지 않을 수 있었다. C, B, C.map, C.each 등의 모든 함수들은 동기로도 동작하고 비동기로도 동작한다. 그러므로 _.map 처럼 즉시 값을 리턴할 수도 있고 Promise.map 처럼 비동기를 제어할 수도 있다. 이 특징은 타 라이브러리와의 편리한 조합을 가능하게 한다.
246 |
247 | Promise와 abcjs의 대표적인 부분들을 비교해보았다. abcjs는 Promise를 대체하기 위해 만들어진 라이브러리가 아니다. abcjs는 앞서 설명한 것처럼 Promise를 파이프라인 내부에서도 지원하고 결과 값으로 약식 then을, 정식 Promise가 있다면 정식 then을 리턴한다. 나는 abcjs가 Browser, NodeJS 등 안에서 때로는 즉시, 때로는 Promise, 때로는 Deferred Object, callback, iteratee, predicate, Event, Notification 등의 각기 다른 API를 가진 수많은 기술(gm, AWS, jQuery, React, Angular, Backbone, underscore, pg, async, await)들을 연결하고 제어하는데 효과적일 것이라 생각한다. 필요하다면 사용을 검토해보길 추천한다.
248 |
249 |
250 | 참고 링크
251 | - [자바스크립트의 약속(Promise): 1부 의문점] (https://gamecodingschool.org/2015/05/23/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%95%BD%EC%86%8Dpromise-1%EB%B6%80-%EC%9D%98%EB%AC%B8%EC%A0%90/)
252 | - [자바스크립트의 약속(Promise): 2부 비교] (https://gamecodingschool.org/2015/05/28/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EC%95%BD%EC%86%8Dpromise-2%EB%B6%80-%EB%B9%84%EA%B5%90/)
253 | - [JavaScript 모나드] (http://www.haruair.com/blog/2986)
254 | - [abcjs 콜백] (https://github.com/marpple/abc-functional-javascript/blob/master/example/05.%20Async%20(callback).html)
255 | - [abcjs Promise] (https://github.com/marpple/abc-functional-javascript/blob/master/example/06.%20Async-2%20(Promise).html)
256 | - [bluebirdjs] (http://bluebirdjs.com/docs/getting-started.html)
257 |
258 |
259 | --------------
260 |
261 | [다른 글 보기] (https://github.com/marpple/abc-functional-javascript/tree/master/docs)
--------------------------------------------------------------------------------
/example/01. A.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - A
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/example/02. B.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - B
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/example/03. C.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - C
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/04. Pipeline with ABC.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - Pipeline with ABC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/example/05. Async (callback).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - Async (callback)
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
239 |
240 |
241 |
242 |
--------------------------------------------------------------------------------
/example/06. Async-2 (Promise).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - Async-2 (Promise)
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/example/07. each...map...find....html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - each...map...find....html
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/example/08. HTML Template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - template
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/example/08.1. HTML Template(TAB_SIZE 4).html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - template
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/example/09. IF ELSEIF ELSE.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - if else
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/example/10. B.all B.spread.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - B.all B.spread
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/example/11. this.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - this
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/example/13. CATCH.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - CATCH
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
202 |
203 |
204 |
205 |
--------------------------------------------------------------------------------
/example/14. Test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/example/15. ETC.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/16. Arrow functions.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/example/17. SEL.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/18. Noti.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/example/box/1. Server Redering.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | abcjs - TEST
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
31 |