logo
Search검색어를 포함하는 게시물들이 최신순으로 표시됩니다.
    Table of Contents
    [functional JS ES6+] 총정리

    이미지 보기

    [functional JS ES6+] 총정리

    • 22.06.11 작성

    • 읽는 데 9

    TOC

    기본 용어

    평가(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값을 처리
    profile

    FE Developer 박승훈

    노력하는 자는 즐기는 자를 이길 수 없다