FxJS는 ECMAScript 6 언어의 Iterable / Iterator, Generator, Promise를 기반으로 작성된 함수형 자바스크립트 라이브러리입니다.
- Modern Browser (>= 2% and last 2 versions)
<script src="https://unpkg.com/fxjs/dist/fx.js"></script>
- Legacy Browser (IE11)
<script src="https://unpkg.com/fxjs/dist/fx.es5.js"></script>
- Usage
주의: 브라우저에서
<script> const { L, C } = window._; _.go( [1, 2, 3], L.map(a => a * a), L.map(_.delay(300)), C.takeAll, _.reduce(_.add), console.log ); // 약 300ms 후에 14 출력 </script>
fx.js
스크립트 파일이 로드되면_
가 전역 변수로 사용됩니다.
FxJS는 CommonJS와 ES6 Module을 함께 지원하는 Dual Module Package 입니다.
FxJS 패키지의 두 가지 모듈 형식 중에서 commonjs는 node.js 6
버전 이상 부터 지원하며, ESM은 node.js 12
버전 이상 부터 사용가능 합니다.
npm install fxjs
- CommonJS (>= node v6)
const FxJS = require("fxjs"); const _ = require("fxjs/Strict"); const L = require("fxjs/Lazy"); const C = require("fxjs/Concurrency"); // fxjs의 default export module 객체는 Lazy, Concurrency를 포함한 모든 함수를 가지고 있습니다. const { reduce, mapL, takeAllC } = FxJS; // 함수를 개별적으로 가져올 수도 있습니다. const rangeL = require("fxjs/Lazy/rangeL"); _.go( rangeL(1, 4), L.map(a => a * a), L.map(_.delay(300)), C.takeAll, _.reduce(_.add), console.log );
- ES6 Module (>= node v12)
import { add, delay, go, reduce, rangeL } from "fxjs"; import * as L from "fxjs/Lazy"; import * as C from "fxjs/Concurrency"; go( rangeL(1, 4), L.map(a => a * a), L.map(delay(300)), C.takeAll, reduce(add), console.log );
FxJS는 Node.js 공식 문서에 소개된 Dual Module Package를 지원하는 두 가지 방법 중 Isolate state 방식을 채택하였습니다. 따라서 아래와 같이 CommonJS 모듈과 ES 모듈을 함께 사용하는 경우에 모듈이나 함수 객체의 동등 비교에 주의해야 합니다. 자세한 내용은 Node.js 공식 문서 를 참고해 주세요.
import { createRequire } from "module";
import * as fxjs_mjs from "fxjs";
import go_mjs from "fxjs/Strict/go.js";
const require = createRequire(import.meta.url);
const fxjs_cjs = require('fxjs');
const go_cjs = require('fxjs/Strict/go');
console.log(fxjs_mjs === fxjs_cjs); // false
console.log(go_mjs === go_cjs); // false
console.log(fxjs_cjs.go === go_cjs); // true
console.log(fxjs_mjs.go === go_mjs); // true
제너레이터를 통해 만든 이터레이터를 FxJS의 함수들로 평가할 수 있습니다.
function* fibonacci() {
let a = 0,
b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const f = pipe(
fibonacci,
L.filter((n) => n % 2 == 0),
L.takeWhile((n) => n < 10)
);
const iterator = f();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 8, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
reduce((a, b) => a + b, f());
// 10
어떤 값이든 [Symbol.iterator]()
메서드를 가진 이터러블이라면 FxJS와 사용할 수 있습니다.
const res = go(
[1, 2, 3, 4, 5],
filter((a) => a % 2),
reduce(add)
);
log(res); // 9
L
네임스페이스의 함수를 통해 지연 평가를 할 수 있습니다.
const res = go(
L.range(Infinity),
L.filter((a) => a % 2),
L.take(3),
reduce(add)
);
log(res); // 9
Functional reactive programming 스타일을 작성할 수 있습니다.
go(
L.range(Infinity),
L.map(delay(1000)),
L.map((a) => a + 10),
L.take(3),
each(log)
);
// 1초 후 10
// 2초 후 11
// 3초 후 12
FxJS의 함수들은 비동기를 잘 다룹니다. Promise의 프로토콜을 잘 따르고 있어 async/await과도 함께 사용할 수 있습니다.
// L.interval = time => L.map(delay(time), L.range(Infinity));
await go(
L.interval(1000),
L.map((a) => a + 30),
L.takeUntil((a) => a == 33),
each(log)
);
// 1초 후 30
// 2초 후 31
// 3초 후 32
// 4초 후 33
const res = await go(
L.interval(1000),
L.map((a) => a + 20),
L.takeWhile((a) => a < 23),
L.map(tap(log)),
reduce(add)
);
// 5초 후 20
// 6초 후 21
// 7초 후 22
log(res);
// 63
C
네임스페이스의 함수를 통해 동시/병렬적인 평가가 가능합니다.
await map(getPage, range(1, 5));
// 4초 후
// [page1, page2, page3, page4]
const pages = await C.map(getPage, range(1, 5));
// 1초 후
// [page1, page2, page3, page4]
L
함수들로 지연해둔 함수열을 C
함수로 동시에 평가할 수도 있습니다. 이런 방식은 Clojure Reducers와 비슷합니다.
go(
range(1, 5),
map(getPage),
filter((page) => page.line > 50),
map(getWords),
flat,
countBy(identity),
log
);
// 4초 후
// { html: 78, css: 36, is: 192 ... }
go(
L.range(1, 5),
L.map(getPage),
L.filter((page) => page.line > 50),
L.map(getWords),
C.takeAll, // 4개 페이지 동시 요청
flat,
countBy(identity),
log
);
// 1초 후
// { html: 78, css: 36, is: 192 ... }
go(
L.range(1, 5),
L.map(getPage),
L.filter((page) => page.line > 50),
L.map(getWords),
C.takeAll(2), // 2개 페이지씩 나눠서 동시 요청
flat,
countBy(identity),
log
);
// 2초 후
// { html: 78, css: 36, is: 192 ... }
FxJS는 자바스크립트의 기본 프로토콜을 지키고 있기 때문에 자바스크립트 표준 에러 핸들링을 사용할 수 있습니다.
const b = go(
0,
(a) => a + 1,
(a) => a + 10,
(a) => a + 100
);
console.log(b);
// 111
try {
const b = go(
0,
(a) => {
throw { hi: "ho" };
},
(a) => a + 10,
(a) => a + 100
);
console.log(b);
} catch (c) {
console.log(c);
}
// { hi: 'ho' }
async/await와 try/catch를 사용하여 비동기 에러 핸들링을 할 수 있습니다. 표준적인 에러 핸들링을 사용하기 때문에 여러 라이브러리들과 함께 사용하기 좋습니다.
const b = await go(
0,
(a) => Promise.resolve(a + 1),
(a) => a + 10,
(a) => a + 100
);
console.log(b);
// 111
try {
const b = await go(
0,
(a) => Promise.resolve(a + 1),
(a) => Promise.reject({ hi: "ho" }),
(a) => a + 100
);
console.log(b);
} catch (c) {
console.log(c);
}
// { hi: 'ho' }
-
- add
- append
- baseSel
- chunk
- compact
- countBy
- deepFlat
- defaults
- defaultTo
- delay
- difference
- differenceBy
- differenceWith
- divide
- drop
- dropRight
- dropUntil
- dropWhile
- each
- entries
- extend
- filter
- find
- findWhere
- flat
- flatMap
- groupBy
- head
- identity
- indexBy
- initial
- insert
- intersection
- intersectionBy
- intersectionWith
- join
- keys
- last
- map
- mapEntries
- mapObject
- max
- maxBy
- mean
- meanBy
- min
- minBy
- multiply
- noop
- object
- omit
- omitBy
- partition
- pick
- pickBy
- pluck
- prepend
- promiseAllEntries
- promiseAllObject
- range
- reduce
- reject
- remove
- repeat
- replace
- sel
- slice
- sort
- sortBy
- sortByDesc
- sortDesc
- split
- splitEvery
- subtract
- sum
- sumBy
- tail
- take
- take1
- takeAll
- takeUntil
- takeWhile
- times
- toIter
- union
- unionBy
- unionWith
- unique
- uniqueBy
- uniqueWith
- unzip
- update
- updateBy
- values
- zip
- zipObj
- zipWith
-
- L.append
- L.chunk
- L.compact
- L.concat
- L.constant
- L.deepFlat
- L.difference
- L.differenceBy
- L.differenceWith
- L.drop
- L.dropUntil
- L.dropWhile
- L.empty
- L.entries
- L.filter
- L.flat
- L.flatMap
- L.insert
- L.intersection
- L.intersectionBy
- L.intersectionWith
- L.interval
- L.keys
- L.limitLoad
- L.map
- L.mapEntries
- L.prepend
- L.range
- L.reject
- L.remove
- L.repeat
- L.reverse
- L.slice
- L.splitEvery
- L.take
- L.takeUntil
- L.takeWhile
- L.times
- L.union
- L.unionBy
- L.unionWith
- L.unique
- L.uniqueBy
- L.uniqueWith
- L.update
- L.updateBy
- L.values
- L.zip
- L.zipWithIndex
위의 라이브러리들은 FxJS를 기반으로 만들어졌습니다. FxSQL과 FxDOM은 각각 SQL과 DOM을 함수형 API를 통해 다룰 수 있는 라이브러리 입니다.