[functional JS ES6+] 총정리

작성:    

업데이트:

카테고리:

태그: , ,

본 포스트는 인프런의 함수형 프로그래밍과 JavaScript ES6+ 강의(링크)를 듣고 정리한 내용입니다.


기본 용어

평가(excuation)

코드 → 계산 → 값


일급함수

  • 함수를 값, 인자, 결과로 취급
  • 유연한 조합과 추상화 가능


고차함수

  • 함수를 값으로 취급하는 함수
  • 함수를 인자로 받는 경우
  • 함수를 만들어 반환하는 경우


iteration

Symbol.iterator

  • idx를 통해 개별 원소에 접근을 가능케 함
  • next 메서드를 반환


iterable

  • iterator를 return하는 Symbol.iterator 메서드를 가지는 값
  • iterable protocol을 따름


iterator

  • next 메서드를 가진 값
  • next 메서드: { value, done } 객체를 return


iterable protocol

  • iterable을 iterable처럼 쓸 수 있게 하는 규약
  • for of문, 전개 연산자 등


well-formed iterator

  • Symbol.iterator를 출력하면 자기 자신이 나오는 iterator
const arr = [1, 2, 3];
let iter = arr[Symbol.iterator]();
log(iter[Symbol.iterator]() == iter); // true


generator

  • iterator이자, iterable을 생성(return)하는 함수
function *gen() {
  yield 1;
  yield 2;
  return 100;
}

let iter = gen();
log(iter.next()); // {value: 1, done: false}
log(iter.next()); // {value: 2, done: false}
log(iter.next()); // {value: 100, done: true}
  • 일반 함수 앞에 ‘*’ 표시
  • yield로 각 단계 표시
  • generator 내부에서 로직을 통해 선택적으로 yield 처리 가능
  • return 값은 done: true에 나오는 값


코드를 값으로 취급 : go, pipe, curry

go

  • 함수 중첩에서 가독성을 위해 사용
  • 인자에 코드 사용(값으로 취급)
  • 이전 인자의 결과를 다음 함수의 인자로 사용
  • 값-함수 묶음
// args의 첫 요소는 항상 시작'값'이므로 args는 시작값
const go = (...args) => reduce((a, f) => f(a), args);

go(
  0,
  a => a + 1,
  a => a + 10,
  a => a + 100,
  log); // 111


pipe

  • 내부에서 go를 실행하는 함수
  • 완전한 함수 묶음
  • 값은 외부에서 함수의 인자로 받음
// 함수들 목록을 인자로 받는데, 초기값을 나중에 받고, 이를 go 함수에 넣어 실행
const pipe = (...fs) => (a) => {go(a, ...fs)};

const f = pipe(
  a => a + 1,
  a => a + 10,
  a => a + 100);

log(f(0));


curry

  • 값을 받아 내가 원하는 시점에 평가

2개 이상의 인자를 받았을 때 함수를 실행하는 curry 함수 코드

const curry = f => 
  (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);

const multi = curry((a, b) => a * b);
log(multi(3)); // (..._) => f(a, ..._)
log(multi(3)(2)); // 6


지연성

  • 어떠한 iterable을 여러 함수 중첩으로 처리할 때 모든 배열을 매번 평가하지 않는 것
  • 필요할 때마다 하나씩 평가해서 처리
  • 게으르다는 의미의 Lazy 평가


즉시 평가

image

매 함수 단계마다 모든 배열을 평가


지연 평가

image

  • 함수 중첩의 모든 지연성을 확인
  • 다음 함수에 연산을 넘기게 됨
  • 뒤의 조건을 충족한다면 불필요한 평가 회피 가능


비동기/동시성

비동기

  • 자바스크립트는 동기적
  • 시간 지연 등 시간이 필요한 작업은 따로 분류해 나중에 처리
  • 이를 기다렸다가 결과를 받도록 하는 것이 비동기 처리
  • 기존에는 callback 함수를 통해 평가된 값을 받을 때까지 기다려서 처리


function add10(a, callback) {
  setTimeout(() => callback(a + 10), 100);
}

add10(5, res => {
  log(res); // 15
});

add10(5, res => {
  add10(res, res => {
    add10(res, res => {
      log(res); // 35
    });
  });
});


Promise

  • 시간이 소요되는 작업의 비동기를 보장
function add20(a) {
  return new Promise(resolve => setTimeout(() => resolve(a + 20), 100));
}

add20(5)
  .then(log); // 25

add20(5)
  .then(add20)
  .then(add20)
  .then(log); // 65
  • callback 함수에 비해 깊이가 깊어지지 않음
  • 비동기 상황을 값으로 취급
  • Promise instance 반환
  • 코드 진행에 대한 상황 파악 가능


Monad

함수 합성에서의 안전성 보장

  • Promise: 비동기 상황에서 사용하는 모나드
  • 기존 monad는 map chain, Promise는 then chain
  • 함수 합성에서 안전성 보장이지만, 인자가 없는 경우가 아닌 대기 상황(비동기 상황)에서의 안전성 보장


Kleisli Composition

오류가 있을 수 있는 함수 합성에서의 안정성을 보장하는 하나의 규칙


지연된 함수열의 병렬적 평가

  • 자바스크립트는 싱글스레드이기 때문에 동시에 여러 작업은 불가능하고 비동기 IO로 처리
  • 하나의 스레드에서도 CPU를 점유하는 작업을 보다 효율적으로 처리
  • 한 node 환경이 아니라, 네트워크나 브라우저, 기타 IO 등 외부 환경으로 작업을 보내 한 번에 처리한 뒤, 이 결과를 받음


C : Concurrency 병행성

const C = {};
C.reduce = curry((f, acc, iter) => iter ?
  reduce(f, acc, [...iter]) :
  reduce(f, [...acc]));
  • iterator가 있는 경우 […iter]를, 없는 경우 …acc를 reduce에 인자로 전달
  • 비동기성을 고려하지 않고, 모든 배열을 평가해 reduce로 넘긴다는 것


console.time('');
go([1, 2, 3, 4, 5],
  L.map(a => delay1000(a * a)),
  L.filter(a => a % 2),
  C.reduce(add),
  log,
  _ => console.timeEnd('')); // 1005.9492...ms
  • L.map과 L.filter를 사용하므로 go함수 내부에서 세로(함수별로 하나씩) 방향으로 작업을 처리
  • 때문에 기존 reduce를 사용하면 매번 delay1000 함수 실행
  • C.reduce를 활용하면 배열 내 요소 각각마다 delay1000 함수를 실행하는 것이 아니라, 배열을 펼쳐 한 번에 평가 후 다음 함수로 전달


async-await

Promise를 return하는 함수에서 보다 동기적으로 코드를 작성하는 방법

function delay(a) {
  return new Promise(resolve => setTimeout(() => resolve(a), 500));
}

async function f1() {
  const a = await delay(10);
  const b = await delay(5);
  log(a); // 10
}

f1();
  • async로 표시한 함수 내부에서 지연되는 함수 또는 작업의 앞에 await를 붙임
  • 쉽게 말하면 await는 Promise 객체를 가능한 순간에 값으로 평가
  • 해당 Promise를 반환하는 statement는 비동기적으로 값으로 평가될 때까지 지연
  • 위의 코드에서 await가 없다면 Promise 객체가 출력, await가 있다면 계산된 10이 출력

주의사항 : async 함수는 Promise 객체를 return ⭐

  • 때문에 async 함수 내부에서 처리된 값을 함수 내부에서만 사용한다면 무관
  • 하지만 async 함수의 결과값(return값)을 외부에서 사용하게 된다면 .then을 통해 외부에서 return값을 처리

댓글남기기