인프런 커뮤니티 질문&답변

yus님의 프로필 이미지
yus

작성한 질문수

함수형 프로그래밍과 JavaScript ES6+

지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take [2]

[이해내용 공유] Lazy 에서 C.reduce가 병렬 효과를 내는 이유

작성

·

554

·

수정됨

3

주요 전제:

  1. 프로미스는 객체가 생성됨과 동시에 콜백을 실행 시킵니다.

  2. then()으로 생성된 프로미스 객체는 로깅하던(자신이 지켜보는) 프로미스가 해결되면 microtaskQueue에 담깁니다.

 

예시

go([1,2],
  L.map(a=>delay1000(a*a),
  L.map(a=>delay1000(a*a),
  C.reduce(add),
  log)

위와 같은 코드가 있다고 할 때, 각 함수층에서 생성하는 프로미스를 다음과 같이 표현해보았습니다.스크린샷 2023-05-17 오후 7.00.59.png

*화살표는 각 프로미스가 로깅하는 자신의 앞의 프로미스를 가리킵니다. ( p1 이 resolve 되면 p2 실행 )
*각 함수와 같은 행에 있는 값은 해당 함수가 생성한 값을 가리킵니다.

 

C.reduce가 전개연산자를 활용하여 제너레이터의 모든 값을 받아올 때, 제너레이터는 순차적으로 값을 내뱉을 것입니다.

C.map은 바로 위의 L.map 으로부터 값을 받아오려 합니다. 그러나 2층의 L.map도 제너레이터를 받아서 돌아가는 제너레이터이므로, 바로 자신의 위인 3층의 L.map으로부터 값을 받아오려합니다.

3층의 Lmap은 자신이 받아오는 제너레이터인 [1,2]에서 1을 받아오고, 함수를 실행시킵니다. 함수는 프로미스를 생성하므로, 프로미스 p1의 콜백이 백그라운드에서 즉시 실행됩니다. Lmap은 자신이 아래층에 생성한 프로미스 객체 p1을 2층의 Lmap에게 내려줍니다.

3층에서 내려준 프로미스 객체를 받은 2층 Lmap은 프로미스가 resolve 되면 자신의 함수를 실행 시킬 것이라는 새로운 프로미스 p2를 생성하여 1층의 C.reduce로 내려줍니다.

C.reduce는 전개연산자로 값을 배열에 다 모을 때까지 프로미스가 들어오든말든 개의치 않습니다. 일단 2층에서 받은 프로미스객체를 배열에 담아둡니다.

C.reduce는 첫번째 값을 받았으니, 다시 2층에게 두번째 값을 요청합니다. 아까와 같은 방식으로 맨 위층에서부터 2를 받고 p4를 내려주고, p4를 받고 p5를 내려주는 형태로 일단은 C.reduce에는 [p2, p5]가 담길 것입니다.

 

이제 C.reduce는 전개연산자로 필요한 값을 다 받았으니, reduce로 모두 더할 차례입니다. 그런데 첫번째 값을 보니 프로미스입니다 (p2). reduce~go1 에 따라 p2가 완료되면 함수를 실행하겠다는 프로미스 p3를 생성하고 reduce 값으로 반환됩니다.

reduce가 반환된 값이 go에 전달되고 최종적으로 콜 스택은 비워집니다. microtaskQueue를 보니, 아까 p1,p4가 실행한 자신의 콜백이 완료되어 p2의 콜백p5의 콜백이 담겨있니다.

콜 스택이 비워져 있으므로, p2와 p5의 콜백이 순차적으로 실행됩니다.

p2가 실행되고 완료됨에 따라 microtaskQueue에는 p3의 콜백이 추가됩니다.

이어서 p3 가 실행됩니다. p3는 첫번째 값의 프로미스가 해결되기를 기다리는 객체였습니다. 이어서 2번째 값을 다루려고 보니 이 역시 프로미스 (p5) 입니다. 따라서 완료되면 사용하겠다는 프로미스 p6를 생성하고 리턴합니다. (p6에는 go2와 recur에 의해 체이닝이 2개지만 일단 p6로 봅니다)

microtaskQueue 에는 p6가 담기고, 콜 스택이 비워졌으므로 즉시 실행됩니다.

go 함수가 마지막 까지 진행되고 결과값이 출력됩니다.


 

여기서 볼 수 있는 점은 생성한 프로미스의 객체가 resolve 되는 순서에 의해, 체이닝된 프로미스들의 콜백이microtaskQueue에 추가되는 시점이 다르다는 것입니다.

프로미스가 생성된 순서는 p1,p2,p3,p4,p5,p6 순이지만,
자신의 선행된 프로미스가 누구냐에 따라 실행되는 순서가 다르고,
그로인해 실행순서는 p1,p4, p2,p5, p3,p6 순 임에 주목해야합니다.

즉, 맨 위에 있는 프로미스가 resolve 되기 전까지,
그 아래층에 있는 프로미스의 콜백은 microtaskQueue 에 추가 될 수 없습니다. 이런 원리는 한 행이 병렬적으로 수행되도록 만들어 내는 것입니다.




답변 2

0

유인동님의 프로필 이미지
유인동
지식공유자

멋집니다 :)

0

안녕하세요, 인프런 AI 인턴이에요.

Lazy에서 C.reduce가 병렬 효과를 내는 이유는, 각 층에서 생성한 프로미스 객체가 resolve 되는 순서에 따라서 자신의 선행된 프로미스가 누구냐에 따라 실행되는 순서가 달라지기 때문입니다. 맨 위에 있는 프로미스가 resolve 되기 전까지, 그 아래층에 있는 프로미스의 콜백은 microtaskQueue에 추가될 수 없습니다. 이런 원리가 한 행이 병렬적으로 수행되도록 만들어 낼 수 있게 됩니다. 감사합니다.

yus님의 프로필 이미지
yus

작성한 질문수

질문하기