What the Functional Programming???(feat. Promise) - adelakim5/fe-w4-martian GitHub Wiki

사싀 λ‚˜λŠ” class만 λ§Œλ“€μ–΄λ΄€μ§€ 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°λ‹€μš΄ μ½”λ“œλ₯Ό 짜본 적은 μ—†μ—ˆκ³ , 였히렀 ν•¨μˆ˜λ‘œ 잘게 μ§œμ„œ μ‘°ν•©ν•΄λ‚˜κ°€λŠ”κ²Œ 더 재밌던 그런 λ‚˜ μžμ‹ μ— λŒ€ν•΄ μ–΄μ©Œλ©΄ ν•¨μˆ˜ν˜•μ— 더 잘 λ§žλŠ” μ‚¬λžŒμΈκ±΄κ°€ μ‹Άμ—ˆλŠ”λ°... 이번 λ―Έμ…˜μ„ ν•˜λ©΄μ„œ μ„ μ–Έμ μœΌλ‘œ μ½”λ“œλ₯Ό μ§ λ‹€λŠ” 것이 κ½€ λ‚―μ„€λ‹€λŠ” 것을 κΉ¨λ‹¬μ•˜λ‹€. (κ·Έλ ‡κ²Œ λ”μ°ν•œ ν˜Όμ’…μ΄ λ˜μ–΄κ°€λŠ” 쀑인듯..)

λ”°λΌμ„œ 이번 λ―Έμ…˜μ„ 기회둜 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•΄ μ΄ν•΄ν•˜λŠ” 깊이λ₯Ό 쑰금 더 λ†’μ΄κΈ°λ‘œ ν•΄λ³Έλ‹€!

λͺ©μ°¨

  1. 이번 λ―Έμ…˜μ—μ„œ λ‚΄κ°€ μ‹œλ„ν•œ 것듀
  2. ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ 뭐라고 μƒκ°ν•˜λ‚˜?
  3. Promise에 λŒ€ν•΄ 깨달은 것듀

이번 λ―Έμ…˜μ—μ„œ λ‚΄κ°€ μ‹œλ„ν•œ 것듀

κ΅¬ν˜„ κ³Όμ •μ—μ„œ μžμ§ˆκ΅¬λ ˆν•œ μ‹œλ„λ“€λ„ λ§Žμ•˜μ§€λ§Œ 일단 λ‚˜λ¦„ κ΄„λͺ©(?)ν• λ§Œν•œ μ‹œλ„λ“€μ„ μΆ”λ €λ³΄μ•˜λ‹€.

MyPromise

mdn의 Promiseλ₯Ό 보고 μ§„λ˜λ°°κΈ°μŠ€λŸ¬μš΄ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ§Œλ“€μ–΄λ³΄κ³  μ‹Άμ—ˆλŠ”λ°, λ‚΄ μ—­λŸ‰μœΌλ‘  ν˜„μ €νžˆ λΆ€μ‘±ν•œ 것 κ°™μ•„μ„œ γ…œγ…œ

사싀상 then(), catch()만 μ‚¬μš©ν•˜λŠ” ν—ˆμ ‘ ν”„λ‘œλ―ΈμŠ€λ₯Ό κ΅¬ν˜„ν–ˆλ‹€.

const pipe = (...fns) => (acc) => fns.reduce((a, f) => f(a), acc);
// resolve둜 λ“€μ–΄μ˜¨ accκ°€ then으둜 λ“€μ–΄μ˜€λŠ” callbackν•¨μˆ˜λ“€μ— μ˜ν•΄ μƒˆλ‘œμš΄ κ°’μœΌλ‘œ 쓰여지도둝 ν•˜λŠ” ν•¨μˆ˜

export default class MyPromise {
  constructor(fn) {
    this.cbList = [];
    this.catchCb = (e) => {
      throw e;
    };
    // fulfilled, rejected
    this.state = "pending";

    setTimeout(() => {
      try {
        this.state = "fulfilled";
        fn(pipe(...this.cbList), this.catchCb); 
      } catch (e) {
        this.state = "rejected";
        this.catchCb(e);
      }
    }, 0);
  }

  then(callback) {
    this.cbList.push(callback);
    return this;
  }

  catch = (callback) => {
    this.catchCb = callback;
    return this;
  };
}
  1. MyPromiseλ₯Ό 생성할 λ•Œ resolve에 λ“€μ–΄μ˜¨ 리턴 값은 pipeν•¨μˆ˜λ₯Ό 톡해 thenμ—μ„œ λ“€μ–΄μ˜¨ μ½œλ°±ν•¨μˆ˜λ“€μ΄ λͺ¨μΈ cbList의 ν•¨μˆ˜λ“€μ„ 순차적으둜 거치며 λ¦¬ν„΄ν•œλ‹€.
    • MyPromise의 μƒνƒœλŠ” fulfilled
  2. 반면 reject둜 λ“€μ–΄μ˜¨ κ°’(reason)은 catchν•¨μˆ˜λ₯Ό 톡해 catchCb둜 λ“±λ‘ν•˜κ³ , μ—λŸ¬κ°€ 났을 λ•Œ ν˜ΈμΆœν•œλ‹€.
    • MyPromise의 μƒνƒœλŠ” rejected

function adela

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ νŠΉμ§• 쀑 ν•˜λ‚˜λŠ” μ‚¬μ΄λ“œμ΄νŽ™νŠΈκ°€ 없도둝, μˆœμˆ˜ν•¨μˆ˜λ“€λΌλ¦¬μ˜ μ‘°ν•©μœΌλ‘œ μ–Έμ œλ‚˜ λ™μΌν•œ output을 λ‚΄λŠ” 것인데, λ‚΄κ°€ κ΅¬ν˜„ν•œ λͺ‡λͺ‡ ν•¨μˆ˜λ“€μ€ λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό μ˜μ‘΄ν•˜λŠ” μ‹μœΌλ‘œ κ΅¬μ„±λ˜μ–΄μžˆμ—ˆλ‹€.

κ·Έλž˜μ„œ μ›λž˜ κΏˆμ€ ν•¨μˆ˜λ₯Ό ν•©μ„±μ‹œμΌœ μƒˆ ν•¨μˆ˜λ₯Ό λ‚΄λŠ” ν•¨μˆ˜λ₯Ό 짜고, 이걸 ν† λŒ€λ‘œ λ‚΄κ°€ μœ„μ—μ„œ λ§ν•œ 그런 ν•¨μˆ˜λ“€μ„ μƒμ„±ν•΄λ‚΄λŠ” λ‘œμ§μ„ 짜고 μ‹Άμ—ˆλŠ”λ° 아직은 λ„˜λ‚˜ μ›λŒ€ν•œ 꿈인 것 κ°™μ•˜κ³ ..

그보닀 μ’€ 더 μž‘μ€ λ‹¨μœ„μ˜ ν•©μ„±ν•¨μˆ˜(?)λ₯Ό λ§Œλ“€μ–΄λ³΄μ•˜λ‹€.

const adela = (f, ...fns) => (...args) =>
  args.length
    ? f(
        ...fns.reduce((acc, fn, i) => {
          acc.push(fn(args[i]));
          return acc;
        }, [])
      )
    : f(...fns);

이름을 뭘둜 μ§€μ„κΉŒ ν–ˆλŠ”λ°, μ΄λ•Œ 컀링을 κ²€μƒ‰ν•΄μ„œ ν•˜κ³  있던 터라.. 컀리도 μ‚¬λžŒ 이름인데, λ‚˜λ„ κ·Έλƒ₯ λ‚΄ μ΄λ¦„μœΌλ‘œ ν•¨μˆ˜λ₯Ό λ§Œλ“€μ—ˆλ‹€ γ…‹γ…‹γ…‹γ…‹

μ € ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•  수 μžˆλ‹€.

const findTarget = (elements, capital) => Object.entries(elements).find((item) => item[1].dataset.id === capital);
const getHTMLElements = (className) => document.querySelectorAll(`.${className}`);
const capital = (letter) => letter.toUpperCase();
// ...
const target = adela(findTarget, getHTMLElements, capital)("line__text", letter);

ν’€μ–΄μ“°λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

const elements = getHTMLElements("line__text");
const capital = capital(letter);
const target = findTarget(elements, capital); 
// === const target = adela(findTarget, getHTMLElements, capital)("line__text", letter);

μ΄λŸ°μ‹μœΌλ‘œ ν•¨μˆ˜ μ•ˆμ—μ„œ λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό λ‹€λ₯Έ μΈμžμ™€ ν•¨κ»˜ ν˜ΈμΆœν•΄μ„œ λ§Œλ“  인자λ₯Ό λ°›κ³  싢을 λ•Œ μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜μ΄λ‹€. 쑰금 λ³΅μž‘ν•œ λŠλ‚Œλ„ μ—†μ§€ μ•Šμ§€λ§Œ, μ‹œλ„ν•΄λ³΄μ•˜λ‹€!

μ΄λ ‡κ²Œ ν•¨μˆ˜ 합성에 λŒ€ν•œ 열망이 λΆˆνƒ€μ˜€λ₯΄λ‹€ λ³΄λ‹ˆ, ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ„ κ³΅λΆ€ν•˜κ³  싢은 생각이 μƒ˜μ†ŸκΈ° μ‹œμž‘ν–ˆλ‹€.

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ 뭐라고 μƒκ°ν•˜λ‚˜?

ν•¨μˆ˜λ“€μ˜ μ‘°ν•©? ν•¨μˆ˜λ“€μ˜ μ§‘ν•©?

ν•¨μˆ˜λ“€ κ°–κ³  μ—¬κΈ°λ‹€ μ“°κ³  μ €κΈ°λ‹€ μ“°λŠ”λ°, 사싀 인자만 μ •ν™•ν•˜κ²Œ κ°€μ Έλ‹€ μ£Όλ©΄ λ‚΄κ°€ μ˜ˆμƒν•˜λŠ” 결과값을 λ¦¬ν„΄ν•˜λŠ” 것?

λ”°λΌμ„œ ν•¨μˆ˜λ“€μ΄ μˆœμˆ˜ν•¨μˆ˜μ—¬μ•Όν•˜κ³ , μ „μ—­λ³€μˆ˜μ™€ 같은 μ°Έμ‘°κ°€ 사싀상 ν•¨μˆ˜ μ™ΈλΆ€μ—λŠ” μ‘΄μž¬ν•˜μ§€ μ•Šμ€ μ±„λ‘œ ν”„λ‘œκ·Έλž˜λ°μ„ μΏ΅μž‘μΏ΅μž‘ ν•˜λŠ” 게 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μΈκ²ƒ κ°™λ‹€. (κ·Έλž˜μ„œ 선언적이라고 λ§ν•˜λŠ” 것 κ°™κ³ ..)

μ–„νŒν•œ μ½”λ”©μ‚¬μ „μ˜ 말을 빌리면,

  • λͺ…λ Ήν˜•: λ„ˆλŠ” μ΄κ±°ν•˜κ³ , λ„ˆλŠ” μ €κ±°ν•˜κ³ , μŸ€λŠ” μ €μͺ½μ— μžˆλŠ”κ±°ν•˜κ³ , μ € μΉœκ΅¬λŠ” 저어어어어기에 μžˆλŠ”κ±° ν•˜λ„λ‘ ν•΄
  • μ„ μ–Έν˜•: μ΄κ±°λŠ” 이거야.

μ΄λ ‡λ‹€λ³΄λ‹ˆ, ν•¨μˆ˜λ₯Ό "κ°’"으둜 바라볼 수 μžˆμ–΄μ•Ό ν•œλ‹€.

κ°’μœΌλ‘œ 바라본닀? 그럼 λ‹€λ₯Έ ν•¨μˆ˜μ— "인자"둜 넣어쀄 수 μžˆλ‹€. λ¬Όλ‘  리턴도 κ°€λŠ₯~ ν•¨μˆ˜ μ•ˆμ—μ„œ μƒˆλ‘œμš΄ ν•¨μˆ˜ λ§Œλ“€μ–΄λ‚΄λŠ” 것도 κ°€λŠ₯~

이런 λ‚΄μš©λ“€μ„ μ•Œμ•„κ°€λ‹€λ³΄λ‹ˆ.. λ‚΄κ°€ κ°κ°ν•˜κ³  있던 Promise에 λ¬Έμ œκ°€ μžˆμ—ˆλ‹€λŠ” 것을 κΉ¨λ‹«κ²Œ λ˜μ—ˆλ‹€.

Promise에 λŒ€ν•΄ 깨달은 것듀

κ·Έ 전에 λͺ¨λ‚˜λ“œλΌλŠ” κ°œλ…μ— λŒ€ν•΄ μ•„μ£Ό 살짝 μ•Œκ²Œλ˜μ—ˆλ‹€.

μœ μΈλ™λ‹˜ 말씀에 μ˜ν•˜λ©΄,

  • ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œλ„ λͺ¨λ‚˜λ“œλ₯Ό μ€‘μ‹œν•˜λŠ” ν”„λ‘œκ·Έλž˜λ°κ³Ό λͺ¨λ‚˜λ“œκ°€ μ—†λŠ” ν”„λ‘œκ·Έλž˜λ°μ΄ μžˆλ‹€.
  • λͺ¨λ‚˜λ“œλŠ” λ©”μ„œλ“œλ₯Ό κ°€μ§„ "κ°’"
  • μ’€ 더 νƒ€μž…μ„ μ€‘μ‹œν•˜λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μ‚¬μš©ν•˜λŠ” "κ°’"

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ€ ν•¨μˆ˜λ₯Ό ν•©μ„±ν•˜λ©° ν”„λ‘œκ·Έλž˜λ°μ„ ν•˜κ²Œ λ˜λŠ”λ°,

f(g(x)) = f(g(x)) // μˆ˜ν•™μ˜ 세계에선 무쑰건 μ΄κ²ƒλ§Œ κ°€λŠ₯
f(g(x)) = x // ν˜„μ‹€ 세계에선 가끔 이런 경우 쑴재

이런 이유둜 μ•ˆμ „ν•œ ν•¨μˆ˜ν•©μ„±μ„ μœ„ν•΄μ„œ λͺ¨λ‚˜λ“œκ°€ μ‘΄μž¬ν•œλ‹€.

λͺ¨λ‚˜λ“œ?

[1] // λ°•μŠ€ μ•ˆμ— μžˆλŠ” "κ°’"

이게 λͺ¨λ‚˜λ“œ.

λͺ¨λ‚˜λ“œλ₯Ό ν•©μ„±ν•œλ‹€λŠ” 말은

const g = a => a + 1;
const f = a => a * a;

const m = [1].map(g).map(f); // 1에 gλ₯Ό μ‹€ν–‰ν•˜κ³ , fλ₯Ό μ‹€ν–‰ν•˜λŠ” λͺ¨λ‚˜λ“œ
//

Promise === future λͺ¨λ‚˜λ“œ

const m = [1].map(g).map(f).forEach(a => console.log(a));
const p = Promise.resolve(1).then(g).then(f).then(a => console.log(a));
// Promise.resolve(1) === [1]
// Promise.reolve(1) === λͺ¨λ‚˜λ“œ

λ§Œμ•½ x값에 λ¬Έμ œκ°€ 생겼닀면?

// example
f(g(x)) = [] // λΉˆλ°°μ—΄μ΄ λ“€μ–΄μ˜΄

이런 경우, μΌλ°˜μ μœΌλ‘œλŠ” κ·Έλƒ₯ ν˜λŸ¬κ°€κ²Œ λ‘μ—ˆλ‹€λ©΄,

ν”„λ‘œλ―ΈμŠ€λŠ” λ‹€μŒκ³Ό 같이 λ°”κΏ”μ£ΌκΈ° μœ„ν•΄ 생겼닀.

f(g(x)) = g(x)
  • g(x)λ₯Ό ν–ˆλŠ”λ° μ—λŸ¬ λ°œμƒ! => fλŠ” μ‹€ν–‰ μ•ˆν•˜κ³  κ·Έλƒ₯ g(x)ν•œκ²ƒκ³Ό λ˜‘κ°™κ²Œ λŒμ•„κ°ˆλž˜!
  • μ—λŸ¬κ°€ λ°œμƒν•œ κ²½μš°μ—λŠ” μ €λ ‡κ²Œ μ‹€ν–‰μ‹œν‚¨λ‹€.
const fg = x => Promise.resolve(x).then(g).then(f);

fg(1).then(console.log); // 4 (μ •μƒμ μœΌλ‘œ λ™μž‘)
fg("μ—λŸ¬λ‹·").catch(_ => "μ—λŸ¬μ•Ό...").then(console.log); // μ—λŸ¬ 

ν”„λ‘œλ―ΈμŠ€λ₯Ό μ½œλ°±μ§€μ˜₯을 ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μ •λ¦¬ν•΄μ„œ μ“°λŠ” "객체"라고 μƒκ°ν•˜λ©΄ 잘λͺ»λœ 생각!

ν”„λ‘œλ―ΈμŠ€λŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό κ°’μœΌλ‘œ 닀루기 μœ„ν•΄ μ‚¬μš©λ˜λŠ” 것!

  • μ–΄λ–€ 일이 일어날지 λͺ¨λ₯΄λŠ” 효과λ₯Ό 감싸놓고 λ‚˜λŠ” 이런 ν˜•νƒœμ˜ 값이라고 해놓고
  • 비동기적인 상황과 성곡과 μ‹€νŒ¨λ₯Ό κ°’μœΌλ‘œ λ‹€λ£¨λŠ” λͺ¨λ‚˜λ“œ
  • "κ°’"μž„

setTimeOut으둜 λ™κΈ°μ μœΌλ‘œ μ‹€ν–‰μ‹œν‚€λŠ” 것은 흐름이지 "κ°’"이 μ•„λ‹ˆλ‹€.

const delay = (time, val) => new Promise(resolve => setTimeout(() => resolve(a), time));

delay(100, 5).then(console.log);
const a = delay(100, 4); // 값에 ν• λ‹Ή κ°€λŠ₯
if(a instanceof Promise) {}; // aκ°€ 무엇인지 확인 κ°€λŠ₯
if(true) a.then(fn); // 쑰건에 따라 λ™μž‘ν•˜κ²Œ ν•  수 있음

ν”„λ‘œλ―ΈμŠ€λ₯Ό λ§Œλ“€μ–΄μ„œ μ–΄λ–€ ν•¨μˆ˜μ— μ „λ‹¬ν•˜κ±°λ‚˜ μ–΄λ–€ 둜직과 ν•¨κ»˜ λ‹€λ£° 수 μžˆκΈ°μ— μ˜λ―Έκ°€ μžˆλŠ” 것이닀.

const go1 = (a, f) => f(a);

go1(10, console.log); // 10;
go1(delay(100, 5), console.log); // μ—λŸ¬λ‚¨

μœ„ go1을 λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•  수 μžˆλ‹€.

const go1 = (a, f) => a instanceof Promsie ? a.then(f) : f(a); // aκ°€ ν”„λ‘œλ―ΈμŠ€μ΄λ©΄ then(f)λ₯Ό, κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ f(a)λ₯Ό λ°˜ν™˜ 

참고자료

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°κ³Ό ES6+