지연 평가 + Promise - L.map, map, take


이터러블을 다루는 많은 함수들은 map, filter, reduce를 뼈대로 응용해서 함수합성등을 통해 (ex: L.map + takeAll, flatMap...) 응용 함수들을 만들었습니다.

L.map, map, take는 기본적으로 동기적으로 돌아가는 상황에서만 정상적인 동작을 보장했습니다. reduce, pipe등 비동기상황에서도 동작하는 이런 함수들처럼 L.map, map, take함수들도 비동기적 상황에서도 정상동작하도록 코드를 리팩토링 해보겠습니다.

before - Promise를 인자값으로 L.map 사용

go([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)],
    L.map(a=>a+10),
    take(2),
    log
);//"[object Promise]10", "[object Promise]10"]

process - L.map에서 Promise를 받도록 적용

const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
L.map = curry(function* (f, iter) {
    for (const a of iter) {
        yield go1(a,f);
    }
});
...
go([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)],
    L.map(a=>a+10),
    take(2),
    log
);
//실행결과
//0: Promise {<fulfilled>: 11}
//1: Promise {<fulfilled>: 12}

이제 프로미스에 map의 인자값으로 받은 함수는 적용되어 11,12과 되었습니다. 이제 Promise안에 들어있는 값을 꺼내보도록 합니다.

process - take에서 Promise내부의 값 꺼내어 반환

const take = curry((l, iter) => {
    let res = [];
    iter = iter[Symbol.iterator]();

    return function recur() {
        let cur;
        while (!(cur = iter.next()).done) {
            const a = cur.value;
            if (a instanceof Promise) 
                return a.then(a => (res.push(a), res).length === l ? res : recur())
            res.push(a);
            if (res.length === l) return res;
        }
        return res;
    }();
});

go([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)],
    L.map(a=>a+10),
    take(2),
    log
);//[11,12]

기존 take 함수에서 currentValue(cur.value)이 Promise일 경우 내부의 값을 then을 통해 res Array에 넣어준 뒤 재귀적으로 다시 유명함수 recur()를 호출해 문제를 해결하고 있습니다.

Complete - 이제 map, L.map전부 정상 동작

go(
    [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)],
    map(a => Promise.resolve(a+10)),
    take(2),
    log
)//[11,12]
go(
    [2,3,4],
    map(a => Promise.resolve(a+10)),
    take(2),
    log
)//[11,12