[ECMAScript6] 성공적인 Promise는 중첩되지 않는다.
ES6 Promise
에는 독특한 특징이 있는데, 지난번 글에서는 설명할 타이밍을 잡지 못해서 그냥 넘어갔었다. 이번에 그 특징에 관해 설명하도록 하겠다.
전에 모나드에 관해서 설명하면서 모나드의 가장 기본적인 operator 중 하나인 bind operator는 M[T]
타입의 모나드가 T
타입의 인자를 받아서 M[U]
타입의 값을 리턴하는 함수를 인자로 받아서 M[U]
타입의 모나드로 타입을 진행시킨다1)고 하였다. 하지만 ES6 Promise
의 then
함수에 관해서 설명하면서 then
함수가 받는 콜백이 값을 리턴하면 resolved 된 Promise
가 리턴되고, 값을 throw
하면 rejected 된 Promise
가 리턴된다고 하였다. 즉, then
함수만으로는 모나드를 리턴하는 함수를 통해서 타입을 전진시키는 bind operator를 구현할 수 없으므로 완전한 모나드를 구현하지 못한다. 그렇다면 ES6의 Promise
는 어떻게 Promise
를 전진시킬까?
간단하다. 그냥 then
함수가 인자로 받는 콜백은 Promise
를 리턴해도 된다.
사실 Promise
가 모나드라는 것을 생각하면, 이쪽이 올바른 사용 법이다. 하지만 ES6뿐 아니라 다른 모나드 구현체에서도 bind operator뿐 아니라, 모나드가 아닌 값을 리턴하는 함수. 즉, (M[T], T => U) => M[U]
에 해당하는 함수도 구현한다. 이는 사실 내부적으로 unit operator와 bind operator를 호출하기 때문에 굳이 필요한 함수는 아니다. 그러함에도 이 함수가 존재하는 이유는 실제로 이 구현을 사용하는 경우가 일반적인 bind operator를 사용하는 경우보다 많아서 사용자의 편의를 위해서 제공되는 것일 뿐이다.
그래도 보통은 둘을 같은 이름의 함수로 구현하지는 않고, 다른 이름의 함수로 구현한다. ES6에서는 then
함수가 두 가지 일을 한다. 동적 타입 언어의 특징을 최대한 활용한 것이다.
하지만 then
함수처럼 콜백 함수가 리턴하는 것이 Promise
인지 아닌지에 따라서 동작이 달라지면 실수하기 쉽다. 이것을 ES6는 애초에 Promise
가 중첩되지 못하도록 함으로써 동작에 일관성을 취했다. 즉, Promise
는 Promise
를 값으로 지니지 못한다.
Promise
가 중첩되지 못하게 하는 역할은 Promise.resolve
함수가 한다. resolve
함수의 스펙을 보면 resolve
함수는 받은 인자가 Promise
이고 같은 생성자를 가진다면, 받은 인자 그 자체를 리턴한다고 되어 있다.2) then
함수는 내부적으로 실행된 결괏값을 이용하여 resolve
함수를 호출한 뒤 그 결괏값인 Promise
를 돌려준다.
resolve
함수는 then
함수뿐이 아니라 Promise.all
함수에서도 사용된다. 지난번에 all
함수를 설명할 때는 아직 resolve
함수에 대해서 제대로 설명하지 않아서, Promise
의 iterable을 받아 하나의 Promise
로 zip 한다고 하였다. 하지만 all
함수도 내부적으로 아이템들을 전부 resolve
함수에 넘기기 때문에, iterable에 Promise
가 아닌 다른 값을 넣어서 호출할 수 있다.
1) 말로 풀어쓰려니 복잡한데, 기호로 표현하면 아래와 같다.
(M[T], T => M[U]) => M[U]
댓글
댓글 쓰기