Promises/A+
스펙에 따르면, 이미 bluebired와 같은 라이브러리에서 구현되어 널리 사용됨- Promise는 트리 구조로 동작.
p.then(handler)
,p.catch(handler)
를 이용해서 branch를 추가 new Promise((resolve, reject) => { /* resolver */})
를 이용해서 새로운 promises를 생성resolve(value)
callback은 value를 이용해서 promise를 수행reject(reason)
callback은 p를 거절- 비동기적으로 resolver와 reject를 호출 가능
- Promises는
pending
상태에서 시작하고,fullfilled
되거나rejected
되면settled
로 상태가 변경 - Promises는 오직 한번만 settled 될 수 있으며, settled promise는 deeper branch를 막는 역할을 수행
- promise는 체인의 형태로 여러번 사용 가능
- .then 핸들러나 .catch 핸들러 중 하나만 실행 가능하며, 둘다 수행할 수는 없음
p.catch(fn).catch(fn)
은 예상한 대로 동작하지 않을 수 있음Promise.resolve(value)
는 value를 사용하는 promise 객체를 생성Promise.reject(reason)
은 거절하는 promise를 생성Promise.all(...promises)
은 all() 메소드 내부에 있는 배열의 모든 요소들에서 수행되는 동작들이 실패하지 않았을때만 실행Promise.race(...promises)
은 하나라도 거부되면 나머지도 결과로 reject을 반환
a proxy for a value that will eventually become available
프록시 혹은 프록시 서버는 컴퓨터 네트워크에서 다른 서버 상의 자원을 찾는 클라이언트로부터 요청을 받아 중계하는 서버를 말한다. 미래의 어떤 특정 시점에 사용 가능한 값을 위한 객체라고 이해했다.
fetch('foo')
fetch 메소드는 Promise 객체
를 반환한다. 아래의 예처럼 Promise의 static 메소드인 .then
체인을 만들어서 foo
의 리소스를 가져왔을때 수행될 동작을 구현할 수 있다.
fetch('foo').then(response => /* do something */)
만약 fetch
가 콜백 함수를 사용한다면, 마지막 파라미터로 해당 콜백을 넘기고 수행이 끝날때 마다 실행된다. 일반적으로 비동기식 코드의 흐름(asynchronous code flow conventions)은 첫번째 파라미터로 error를 받아 들인다. 나머지 파라미터로는 실행 결과를 전달 받는다.
fetch('foo', (err, res) => {
if (err) {
// handle error
}
// handle response
})
해당 소스에서는 foo
리소스가 완전히 실행되기 전까지 콜백은 실행되지 않는다. 위와 같은 모델에서는 결과 값에 대해서 하나의 콜백만 등록할 수 있다.
응답을 처리하기 위해서 아래의 예처럼 _event-driven model_을 사용할 수도 있다. fetch
에 의해서 리턴되는 객체를 on
이벤트에 연결한다. 일반적으로 data
이벤트는 성공적으로 수행되었을 경우에 호출되며, error
이벤트는 실패 한 경우에 실행된다.
fetch('foo')
.on('error', err => {
// handle error
})
.on('data', res => {
// handle response
})
이 경우에 예외 처리에 대한 이벤트 리스너가 구현되어 있지 않으면 error는 위험에 처하게 된다.
promises는 .on
을 통해서 이벤트 리스터에 바인딩하지 않는다. fetch
메소드는 Promise
객체를 리턴하는데, Promise
객체의 .catch
와 .then
을 이용해서 이벤트 리스너들을 바인딩 할 수 있다.
var p = fetch('foo')
p.then(res => {
// handle response
})
p.catch(error => {
// handle error
})
.then
은 두번째 argument로 rejection을 등록할 수 있다.
fetch('foo')
.then(
res => {
// handle response
},
err => {
// handle error
}
)
.then(null, rejection)
은 .catch(rejection)
과 동일하게 사용할 수 있다. 그리고 .then
과 .catch
는 항상 새로운 promise 객체를 반환한다. 항상 새로운 promise 객체가 생성되기 때문에 체이닝(chaining)이 가능하다.
example source code
Promises aren’t all that new. Like most things in computer science, the earliest mention of Promises can be traced all the way back to the late seventies. According to the Internet, they made their first appearance in JavaScript in 2007 – in a library called MochiKit. Then Dojo adopted it, and jQuery followed shortly after that.
Then the Promises/A+ specification came out from the CommonJS group (now famous for their CommonJS module specification). In its earliest incarnations, Node.js shipped with promises. Some time later, they were removed from core and everyone switched over to callbacks. Now, promises ship with the ES6 standard and V8 has already implemented them a while back.
The ES6 standard implements Promises/A+ natively. In the latest versions of Node.js you can use promises without any libraries. They’re also available on Chrome 32+, Firefox 29+, and Safari 7.1+.
아래의 소스 코드는 가장 간단한 형태의 promise 예제이다.
fetch('foo').then(res => {
// handle response
})
위 코드에서 에러가 발생하면 어떻게 동작할까?
fetch('foo')
.then(res => res.a.prop.that.does.not.exist)
.catch(err => console.error(err.message))
.catch
를 이용해서 에러를 잡아 낼 수 있다. 그렇다면 아래의 소스 코드는 어떤 결과를 출력할까?
fetch('foo')
.then(res => res.a.prop.that.does.not.exist)
.catch(err => console.error(err.message))
.catch(err => console.error(err.message))
두번째 .catch
는 첫번째 .catch
에 의해 생성된 promise 객체에서 발생하는 error를 다룬다. 그렇다면 에러 메세지를 2번 출력하기 위해서는 어떻게 할까? 다음 소스 코드를 보자.
var p = fetch('foo').then(res => res.a.prop.that.does.not.exist)
p.catch(err => console.error(err.message))
p.catch(err => console.error(err.message))
조금 다른 예제를 보자. 해당 소스 코드에서는 에러에 대한 처리가, 첫번째 .catch
의 rejection branch에 해당하는 두번째 catch에서 이루어 지고 있다.
fetch('foo')
.then(res => res.a.prop.that.does.not.exist)
.catch(err => { throw new Error(err.message) })
.catch(err => console.error(err.message))
아래 코드 처럼 첫번째 .catch
에서 아무것도 리턴하지 않는다면, 아무런 출력이 없다.
fetch('foo')
.then(res => res.a.prop.that.does.not.exist)
.catch(err => {})
.catch(err => console.error(err.message))
promise들은 제멋대로(arbitrarily) 체인이 만들어 질 수 있는지 주시(observe) 해야 한다. 다시 말해서, promise chain에 있는 어떤 지점에 대한 참조를 저장할 수 있고 더 많은 promise들을 chain의 끝에 붙일 수 있다.
You can save a reference to any point in the promise chain.
var p1 = fetch('foo')
var p2 = p1.then(res => res.a.prop.that.does.not.exist)
var p3 = p2.catch(err => {})
var p4 = p3.catch(err => console.error(err.message))
fetch
는 새로운p1
promise 객체를 리턴한다p1.then
은 새로운p2
promise 객체를 리턴한다p2.catch
은 새로운p3
promise 객체를 리턴한다p3.catch
은 새로운p4
promise 객체를 리턴한다p1
이 _fulfilled_이 되어 settled 상태가 되면,p1.then
이 실행된다- After that
p2
, which is awaiting the pending result ofp1.then
is settled p2
가 rejected 상태기 때문에,p2.then
브랜치 대신에p2.catch
가 수행된다.p3
promise는p2.catch
가 특정한 값을 생산하거나 에러를 일으키지 않기 때문에 fulfilled 상태가 된다.p3
는 항상 성공이기 때문에,p3.catch
은 절대 실행되지 않는다.