비동기 I/O는 싱글스레드 기반에서 하나의 스레드에서 CPU를 효율적으로 점유하는 방법이다.
자바스크립트는 싱글스레드이기 때문에 로직을 싱글스레드로만 제어할 뿐 병렬적인 처리도 필요하다.
go([1, 2, 3, 4, 5],
L.map(a => delay500(a * a)),
L.filter(a => delay500(a % 2)),
L.map(a => delay500(a * a)),
reduce(add)
log
)
위 코드는 순차적으로 1이 L.map, L.filter, L.map이 실행되고 reduce는 2가 L.map, L.filter, L.map가 실행될때까지 기다렸다가 연산하고 그 다음 3,4,,,가 인자로 들어가며 작동할 것이다.
reduce를 병렬적으로 처리하기위해 C.reduce를 만들어보자
C.reduce = curry((f, acc, iter) => {
if (iter) {
return reduce(f, acc, [...iter])
} else {
return reduce(f, [...acc])
}
});
C.reduce 위의 map, filter가 모두 실행될 때까지 기다렸다가 이 모든 값을 펼쳐서 reduce를 한번에 실행한다.
L.map, L.filter는 C.reduce에 있는 전개연산자로 인해 프로미스들이 모두 즉시 평가된다.
전개 연산자가 없는 경우 next()가 발생할때마다 값을 하나씩 순차적으로 빼오지만 전개 연산자를 사용하는 경우 next()로 반환 될 값이 한꺼번에 담기게된다.
그러므로 동시적으로 모든 프로미스들이 동작하여 C.reduce프로미스가 처리된 값을 합산만 하여 병렬처리가 가능하게 된 것이다.
위 에러는 전개 연산자를 통해 map,filter가 작동하던 중 생긴 reject가 catch되면서 생긴 에러이다.
즉 로직의 작동결과에는 영향을 미치지 않지만 catch가 되어 불필요한 로그가 찍히고 있다.
reject이 있지만 catch를 하지 않고 넘기기 위해서 아래와 같이 할 수 있다.
중요한 점은 이미 catch가 되어 평가가 끝난 것이 아니라, catch를 걸어놓기만 상태를 유지하여 catch를 다음 시점에 넘겨서 평가 되도록 하는 것이다.
C.reduce = curry((f, acc, iter) => {
const iter2 = catchNoop(iter ? [...iter] : [...acc]);
iter2.forEach(a => a.catch(function() {}))
if (iter) {
return reduce(f, acc, iter2)
} else {
return reduce(f, iter2)
}
});
이렇게 iter2에 reject가 있는 경우 catch에서 아무것도 하지 않도록 하면 에러가 뿜어지지 않게 된다.