Skip to content

Latest commit

 

History

History
582 lines (522 loc) · 25.2 KB

README_kr.md

File metadata and controls

582 lines (522 loc) · 25.2 KB

EN | KR

FxJS - Functional Extensions for Javascript

npm npm bundle size npm NPM

FxJS는 ECMAScript 6 언어의 Iterable / Iterator, Generator, Promise를 기반으로 작성된 함수형 자바스크립트 라이브러리입니다.

Getting Started

Installation

In the browser environment

  • 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 스크립트 파일이 로드되면 _가 전역 변수로 사용됩니다.

In the node.js environment

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
    );

Dual Package Hazard

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

Iteration protocols

제너레이터를 통해 만든 이터레이터를 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

Iterable programming

어떤 값이든 [Symbol.iterator]() 메서드를 가진 이터러블이라면 FxJS와 사용할 수 있습니다.

const res = go(
  [1, 2, 3, 4, 5],
  filter((a) => a % 2),
  reduce(add)
);

log(res); // 9

Lazy evaluation

L 네임스페이스의 함수를 통해 지연 평가를 할 수 있습니다.

const res = go(
  L.range(Infinity),
  L.filter((a) => a % 2),
  L.take(3),
  reduce(add)
);

log(res); // 9

FRP style

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

Promise/async/await

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

Concurrency

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 ... }

Error handling

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' }

API

Extension Libraries

위의 라이브러리들은 FxJS를 기반으로 만들어졌습니다. FxSQL과 FxDOM은 각각 SQL과 DOM을 함수형 API를 통해 다룰 수 있는 라이브러리 입니다.