현실세계로 비유하자면, Promise
는 바우처와 비슷한 개념이다.
재화를 구매했지만 지금 당장은 바우처만 받았고 아직은 내 손에 재화가 없는 상태가 비동기이다. 하지만 재화 대신 받아서 가지고 있는 바우처는 내가 나중에 받게 될 재화와 동등하게 취급된다.
비동기 결과로 만들어지는 Promise
변수 그 자체는 비동기 코드 호출과 동시에 받을 수 있다. 즉 Promise를 받는 코드는 동기적인 코드이다. 실제로 우리가 기대하는 값 (예를들어 API 호출을 통해 받으려는 JSON)은 아직 실제로 받지 못한 상태이지만, 그 값 대신 받은 Promise를 가지고 그냥 쓰면 된다.
const getData = () => {
const result = axios.get('https://sample.com/data') // result 는 실제 데이터가 아닌 Promise다.
return result;
}
const data = getData() // 마치 진짜 데이터를 받은것처럼 이리저리 전달해도 된다. 맨 마지막에 실제로 값을 뽑아낼 때만 잘 처리하면 된다.
Promise의 메서드들 (then, catch, finally)도 Promise를 리턴한다.
그렇기 때문에, Promise 메서드 호출 후, 또다시 Promise 메서드를 호출할 수 있다. promise를 연결해서 작성하는건 무한히 가능하다. Promise메서드를 연결해가며 사용하는걸 Promise chain이라고 부른다.
Promise 메서드의 콜백에서 리턴된 값은 다음에 실행될 체인에서 콜백의 인자의 값이 된다.
Promise.resolve()
.then(() => {
return 12
}).then(e => {
// e === 12
})
Promise 메서드 안쪽 콜백의 실행결과에 따라 Promise는 둘 중 하나의 상태로 귀결된다.
fulfilled
: 비동기 처리가 성공적으로 진행되었다는 뜻이다.rejected
: 비동기 처리가 실패했다는 뜻이다.Promise 메서드의 콜백에서 Promise가 아닌 값을 리턴한다면 (또는 아무것도 리턴하지 않아 undefined를 리턴한 셈이 되었다면), fulfilled
된 것으로 취급한다.
fulfilled
처리된 체인 다음에 오는 체인들 중 제일 처음으로 등장하는 then
이 그것을 받아들이며, 리턴되었던 값을 인자로 받는다.
Promise.resolve()
.then(() => {
return 'hi'
}).catch(() => {
console.log('앞 체인이 fulfilled라면 catch는 무시된다.')
}).then(e => {
// e === 'hi'
console.log('fulfilled로 취급되어 이 코드가 실행된다.')
})
Promise 메서드의 콜백에서 에러가 난다면 rejected
된 것으로 처리한다.
rejected
처리된 체인 다음에 오는 체인들 중 제일 처음으로 등장하는 catch
가 그것을 받아들이며, 발생한 에러를 인자로 받는다.
Promise.resolve()
.then(() => {
null.foo() // 에러가 난다.
}).then(() => {
console.log('앞 체인이 rejected라면 then은 무시된다.')
}).catch(e => {
console.log('이 코드가 실행된다.')
console.error(e) // TypeError: Cannot read property 'foo' of null
})
Promise 메서드의 콜백이 Promise를 리턴할 경우는 , 해당 Promise의 처리 결과가 해당 메서드의 Promise 결과인 것으로 처리된다.
Promise.resolve()
.then(() => {
const result = fetch('htttps://example.com/api') // promise를 받는다.
return result;
}).then(() => {
console.log('앞에 있는 fetch의 promise가 fulfilled로 처리된다면 이 코드가 실행된다.')
}).catch(() => {
console.log('앞에 있는 fetch의 promise가 rejected로 처리된다면 이 코드가 실행된다.')
})
async/await
는 Promise
로 된 비동기 코드를 동기적으로 쓸수 있게 해주는 문법이다.
const getData1 = () => {
const promise = fetch('htttps://example.com/api')
.then(res => {
return res.json()
}).then(data => {
console.log(data)
});
return promise
}
이런 코드는
const getData2 = async () => {
const res = await fetch('htttps://example.com/api')
const data = await res.json();
console.log(data);
return data;
}
async/await
로 이렇게 고칠 수 있다.
함수 앞에 async 키워드를 붙이고, promise 값 앞에 await 키워드를 붙이기만 하면 promise 메서드 없이 값을 사용할 수 있게 된다.
비동기코드같지 않고 동기적 코드같다.
하지만 혼동하면 안되는데, 문법의 생김새가 동기적처럼 보일 뿐이지 실제로는 둘다 똑같은 비동기코드다.
getData1을 실행한 결과와, getData2를 실행한 결과 둘다 Promise
이다.
async 함수의 리턴결과는 항상 Promise이다.