ES6+ Basic5

์ธํ”„๋Ÿฐ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ JavaScript ES6+ / ์œ ์ธ๋™

callback๊ณผ Promise

  • callback : ์ธ์ž์™€ callbackํ•จ์ˆ˜๋ฅผ ๋ฐ›์•„์„œ ์ •์˜ํ•จ

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

add10(5, res => {
    // ์—ฐ์†์‹คํ–‰
    add10(res, res => {
        log(res);
    })
})
  • Promise : Promise๋ฅผ ๋งŒ๋“ค์–ด์„œ returnํ•ด์คŒ

function add20(a) {
    return new Promise(resolve => setTimeout(() => resolve(a+20), 100));
}

add20(5)
    // ์—ฐ์†์‹คํ–‰
    .then(add20)
    .then(log);

callback๊ณผ Promise์˜ ์ฐจ์ด

  • Promise๋Š” ์ผ๊ธ‰ ๊ฐ’์œผ๋กœ ๋น„๋™๊ธฐ ์ƒํ™ฉ์„ ๋‹ค๋ฃธ(์ผ๊ธ‰ = ์ธ์ž, ๋ณ€์ˆ˜, ๋ฆฌํ„ด๊ฐ’ ๋“ฑ์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Œ)

  • Promise๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง„ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ๋Œ€๊ธฐ,์„ฑ๊ณต,์‹คํŒจ๋ฅผ ๋‹ค๋ฃจ๋Š” ์ผ๊ธ‰๊ฐ’์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์Œ

  • Promise๋Š” ์ฝ”๋“œ๋ฅผ ํ‰๊ฐ€ํ–ˆ์„๋•Œ ์ฆ‰์‹œ Promise๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋จ

  • Promise๋Š” ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด ์ดํ›„์— ์ผ๋“ค์„ ์—ฐ๊ฒฐ์ง€์–ด์„œ ํ•ด๋‚˜๊ฐˆ ์ˆ˜ ์žˆ์Œ

  • callback์€ ๋น„๋™๊ธฐ ์ƒํ™ฉ์„ ์ฝ”๋“œ์™€ ์ปจํ…์ŠคํŠธ๋กœ๋งŒ ๋‹ค๋ฃธ(์‹คํ–‰ํ•˜๊ณ  ๋‚˜๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ด์–ด๋‚˜๊ฐˆ ์ˆ˜ ์—†์Œ)

๊ฐ’์œผ๋กœ์„œ Promise ํ™œ์šฉ

const deley100 = a => new Promise(resolve => 
    setTimeout(() => resolve(a), 100));

const go1 = (a, f) => a instanceof Promise ? .then(f) : f(a);
const add5 = a => a + 5;

const n1 = 10;
log(go1(go1(n1, add5), log));

const n2 = deley100(10);
log(go1(go1(n2, add5), log));

ํ•จ์ˆ˜ํ•ฉ์„ฑ ๊ด€์ ์—์„œ์˜ Promise์™€ ๋ชจ๋‚˜๋“œ

  • ํ•จ์ˆ˜ํ•ฉ์„ฑ : f.g == f(g(f))

  • ๋ชจ๋‚˜๋“œ : ํ•จ์ˆ˜ ํ•ฉ์„ฑ์„ ํ• ๋•Œ ์•ˆ์ „ํ•˜๊ฒŒ ํ•ฉ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ(์ค‘์š”X)

  • Promise : ๋น„๋™๊ธฐ ์ƒํ™ฉ์„ ์•ˆ์ „ํ•˜๊ฒŒ ํ•ฉ์„ฑํ•˜๋Š” ๊ฒƒ

Kleisli Composition ๊ด€์ ์—์„œ์˜ Promise

  • f.g f(g(x)) = f(g(x)) ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ์„ ๋•Œ๋Š” ์ด ํ•ฉ์„ฑ์ด ์„ฑ๋ฆฝ๋˜์ง€ ์•Š์Œ

  • Kleisli Composition : ํŠน์ •ํ•œ ๊ทœ์น™์„ ๋งŒ๋“ค์–ด์„œ ํ•ฉ์„ฑ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์ˆ˜ํ•™์ ์œผ๋กœ ๋ฐ”๋ผ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ์ฃผ๋Š” ๊ฒƒ

  • f(g(x)) = g(x) : g์—์„œ ์—๋Ÿฌ๊ฐ€ ๋‚œ ๊ฒฝ์šฐ์—๋Š” ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ์„ฑ๋ฆฝ๋  ์ˆ˜ ์žˆ์Œ(Kleisli Composition)

  • (๊ฐ™์€ Promise๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ์œ„์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ์„ฑ๋ฆฝ๋จ)

  • Promise.reject : ์—๋Ÿฌ๊ฐ€ ๋‚  ๊ฒฝ์šฐ reject๋œ Promise๋ฅผ ๋ฆฌํ„ดํ•จ

go, pipe, reduce์—์„œ ๋น„๋™๊ธฐ ์ œ์–ด

  • ex) test/es6_code.html - 39 line go1()

Promise.then์˜ ์ค‘์š”ํ•œ ๊ทœ์น™

  • ์•„๋ฌด๋ฆฌ ์—ฌ๋Ÿฌ๋ฒˆ์˜ Promise๊ฐ€ ์ค‘์ฒฉ๋˜์–ด๋„ ์•ˆ์ชฝ์— ์žˆ๋Š” '๊ฐ’'์„ ํ•œ๋ฒˆ์˜ then์œผ๋กœ ๊บผ๋‚ผ ์ˆ˜ ์žˆ์Œ

Promise.resolve(Promise.resolve(Promise.resolve(1))).then(log);
// 1

new Promise(resolve => resolve(new Promise(resolve => resolve(1))))).then(log);
// 1

์ง€์—ฐ ํ‰๊ฐ€ + Promise - L.map, map, take

  • ์ง€์—ฐํ‰๊ฐ€์™€ ๋น„๋™๊ธฐ ๋™์‹œ์„ฑ์„ ์ง€์›ํ•˜๋Š” ํ•จ์ˆ˜

const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);

const take = curry((l, iter) => {
    let res = [];
    // ์žฌ๊ท€
    return function recur() {
        for (const a of iter) {
            if (a instanceof Promise) return a
                .then(a =>  (res.push(a), res).length == l ? res : recur())
                // Kleisli Composition ํ™œ์šฉ
                .catch(e => e == nop ? recur() : Promise.recject(e));
            res.push(a);
            if (res.length == l) return res;
        }
        return res;
    } ();
});

L.map = curry(function *(f, iter) {
    for(const a of iter) {
        yield go1(a, f);
});

go(
    // [1, 2, 3]
    [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)],

    // L.map(a => a + 10),
    // L.map(a => Promise.resolve(a + 10)),

    // map์€ L.map, takeAll์„ ํ•˜๋Š” ํ•จ์ˆ˜
    map(a => Promise.resolve(a + 10)),
    // takeAll,
    log);

Kleisli Composition - L.filter, filter, nop, take

  • ํ•„ํ„ฐ์—์„œ ์ง€์—ฐํ‰๊ฐ€์™€ ๋น„๋™๊ธฐ ๋™์‹œ์„ฑ, Promise๋ฅผ ์ง€์›ํ•˜๋ ค๋ฉด Kleisli Composition์„ ํ™œ์šฉํ•ด์•ผํ•จ

  • Promise.reject()๋ฅผ ํ•˜๊ฒŒ๋˜๋ฉด ๋ชจ๋“  .then์„ ๋ฌด์‹œํ•˜๊ณ  .catch๋กœ ๊ฐ

// ์•„๋ฌด์ผ๋„ ํ•˜์ง€์•Š๋Š”๋‹ค๋Š” ๊ตฌ๋ถ„์ž
const nop = Sybol('nop');

L.filter = curry(function *(f, iter) {
    for(const a of iter) {
        const b = go1(a, b);
        if (b isinstanceof Promise) yield b
            .then(b => b ? a : Promise.reject(nop));
        else if (f(b)) yield a;
    }
});

go([1, 2, 3, 4, 5, 6],
    L.map(a => Promise.resolve(a * a)),
    L.filter(a => a % 2),
    L.map(a => a * a),
    take(4),
    log);

reduce์—์„œ nop์ง€์›

  • ์ง€์—ฐํ‰๊ฐ€์™€ ๋น„๋™๊ธฐ ๋™์‹œ์„ฑ์„ ์ง€์›ํ•˜๋Š” reduce

const reduceF = (acc, a, f) => 
    a instanceof Promise ? 
        a.then(a => f(acc, a), e => e == nop ? acc : Promise.recject(e)) : 
        f(acc, a);

const head = iter => go1(take(1, iter), ([h]) => h);

const reduce = curry((f, acc, iter) => {
    if (!iter) return reduce(f, head(iter = acc[Symbol.iterator](), iter);

    iter = iter([Symbol.iterator]());
    return go1(acc, function recur(acc) {
        let cur;
        while(!(cur = iter.next()).done) {
            acc = reduceF(acc, cur.value, f);
            if (acc instanceof Promise) return acc.then(recur);
        }
    });
    return acc;
});

go([1, 2, 3, 4],
    L.map(a => Promise.resolve(a * a)),
    L.filter(a => Promise.resolve(a % 2)),
    reduce(add),
    log);

์ง€์—ฐ ํ‰๊ฐ€ + Promise์˜ ํšจ์œจ์„ฑ

  • ๋ชจ๋“  ๊ฐ’์„ ํ‰๊ฐ€ํ•˜๊ณ  ๊ทธ ์ค‘์—์„œ takeํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์›ํ•˜๋Š” ๊ฐ’์„ takeํ•˜๊ณ  ๋‚˜๋ฉด ๋‚˜๋จธ์ง€๋Š” ์•„์˜ˆ ํ‰๊ฐ€ํ•˜์ง€ ์•Š์Œ

Last updated