L.map과 L.filter가 Promise를 반환하는 함수를 받았을 때도 올바르게 동작해야 한다. 지연 평가와 비동기가 만나는 지점이다.
go와 take가 Promise를 처리할 수 있다면, L.map은 단순히 yield f(a)만 해도 된다.
go(
[1, 2, 3],
L.map(a => Promise.resolve(a + 10)),
take(2),
log
); // [11, 12]
take가 값을 꺼낼 때 Promise를 만나면 .then으로 기다리고, 이후 작업을 이어간다.
L.filter에서는 상황이 다르다. f(a)가 Promise를 반환하면, if (f(a))에서 Promise 자체는 항상 truthy다.
이를 해결하기 위해 nop(no operation) 심볼을 도입했다.
const nop = Symbol('nop');
L.filter = curry(function* (f, iter) {
for (const a of iter) {
const b = go1(a, f);
if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop));
else if (b) yield a;
}
});
Promise가 resolve되어 falsy한 값이면 Promise.reject(nop)으로 해당 값을 건너뛴다. take는 nop으로 reject된 Promise를 만나면 그냥 다음 값으로 넘어간다.
go(
[1, 2, 3, 4, 5, 6],
L.map(a => Promise.resolve(a * a)),
L.filter(a => Promise.resolve(a % 2)),
take(4),
log
); // [1, 9, 25]
Promise 기반 비동기 필터링이 지연 평가와 함께 동작한다.