FP(Functional Programming) - Task

cfop·2020년 10월 19일
0

Task monad

비동기적인 과정이 있을 때 사용 합니다.

우리는 이미 익숙한 비동기를 알고 있습니다. 바로 Promise죠

const pr = () => new Promise((res, rej) => setTimeout(()=> res(1), 1000))
pr.then((res) => console.log(res))
1초 뒤..
=>1

Promise도 일종의 모나드 입니다. 안에 뭐가 들었던 Promise로 항상 mapping되기 때문입니다. 
pr.then().then().then()..

async test function(){
 const res = await pr(); // 1    
}

이렇게도 쓸 수 있죠. 

앞서 소개한 모든 모나드는 비동기 함수는 적용할 수 없습니다

return으로 연결되어 있는 모나드들의 mapping 함수는, 비동기 함수는 적용할 수 없습니다.
사실 동기, 비동기는 어떻게 해도 같이 혼합 될 수가 없습니다.

const pr = () => new Promise((res, rej) => setTimeout(()=> res(1), 1000))
async test function(){
 const res = await pr(); 
 console.log(res) // 1
 return res;
}

test() // Promise(pending..) -> 1초뒤 -> 1

async await 키워드는 async가 붙은 함수 안에서만 동기적 실행을 보장 합니다.
자바스크립트는 비동기를 완전 동기화 할 수 없습니다. Promise 스펙 중에
현재 돌고 있는 이벤트 루프가 비워지지 않으면 Promise는 영원히 resolve 되지 않는다는(task, micro task 를 검색해보시면 자세히 나와 있습니다.)
규칙 때문 입니다. 무슨 수를 쓰더라도 아무데서나 비동기를 동기와 섞어 사용할 수는 없습니다.

그럼 Promise를 사용하지 않고 Task를 사용하면 무슨 차이가 있나요?

const pr = () => new Promise((res, rej) => setTimeout(()=> res(1), 1000))

pr() 하게 되면 Promise가 자동으로 pending 상태에 들어가고 fufilled를 기다립니다.

Promise는 생성되는 순간에 바로 실행 됩니다. 원하는 시점에 Promise 를 사용하고 싶으면
원하는 시점에서 Promise를 생성해서 return 하면 됩니다.

늘 이런 방법만 있는 걸까요?

const task = Task((err, ok) => ok(1)) // 1
.map((value) => 100) // 2
.chain(() => {... }) // 3

//내가 원하는 시점에
task.fork(console.error, console.log) //4
1-> 2-> 3-> 4 순서대로 실행 

이런 식으로도 비동기를 연결해서 실행 할 수 있습니다.

두 번째 차이는

const pr = () => new Promise((res, rej) => setTimeout(()=> res(1), 1000))
const pr2 = () => new Promise((res, rej) => setTimeout(()=> res(2), 1000))

pr이 resolve 되면 pr2를 요청하고 싶어요

pr().then((res)=> pr2().then((res2)=> res2)).then((res)=> console.log(res)) //2

딱 봐도 복잡해 보이네요 .
Task는 이에 반해 Monad이기 때문에 새로운 Task pipeline을 만들고 chaine으로 연결만 하면 됩니다.

const t = monadologia.task<number>((err: Function, ok: Function)=>{
    ok(1)
}).chain((v: number)=> {
	monadologia
    .task<string>((err: Function, ok: Function)=>{ok("")})
    .map((v) => 200
    .fork(console.error, (v) => ok(v))
}).map((v: string)=> 100)

이렇게 각자 별개의 Task도 손 쉽게 연결할 수 있습니다..(똑같아보이나요ㅜㅜ)

Task 모나드의 interface

이제 리턴으로 연결 되지 않고 함수를 받아서 합성하게 됩니다.

Task 모나드의 구현

그렇군요! ok에 들어올 함수에다 합성을 하는군요 ok(f()) 이런 식으로요
error은 받은 그대로 내려줍니다. error 함수는 합성되지 않기 때문에 Task 어느 과정에서 error 함수를 호출하면 거기서 Task는 종료 됩니다.
chain은 Task끼리 합성 합니다.
flatten은 이중 Task를 단일 Task로 flat하게 만들어 줍니다.

예제


1 -> "" -> 100으로 ok의 파라미터로 전달 되고 fork-ok 함수인
console.log에 100이 넘어가 100이 찍히게 됩니다.

Promise도 좋지만 색다른 비동기 다루기 인거 같아 좋습니다. 그러나 상태를
'전달' 하지 '저장' 하지는 않습니다.

이것은 왠지 쓸 곳이 많을 것 같다는 생각이 듭니다.

profile
dog발자

0개의 댓글