1. Promise - 비동기를 값으로 다루는 법
Promise 란?
- 자바스크립트가 미리 만들어둔 하나의 객체
- 이 객체는 then, catch 라는 메서드를 가지고 있다.
- pending, fulfilled, rejected 세가지 상태를 가지고 있다.
Promise는 최초에 pending 상태로 만들어진다. 그리고 이후 fulfilled 또는 rejected 상태로 변경시킬 수 있다. Promise를 만들기 위해서는 new Promise 키워드를 통해서 만들 수 있는데 이때 콜백함수를 인자로 넣을 수 있다.
new Promise((resolve, reject) => {})
이때 콜백 안에서 resolve 함수를 호출하면 Promise는 value와 함께 fulfilled 상태가 된다. 반대로 reject 함수를 호출하면 Promise는 reason과 함께 rejected 상태가 된다. 그리고 fulfilled 또는 rejected 상태가 된 Promise는 then 메서드의 콜백함수를 호출한다.
then 메서드는 두개의 인자를 받는다.
then(onResolved, onRejected);
각각 promise가 resolve되거나, reject되었을 때 실행할 콜백함수를 인자로 받으며, then은 promise의 상태에 따라서 value를 onResolved 함수의 인자로 넣어서 호출하거나, reason을 onRejected 함수의 인자로 넣어서 호출해준다.
const pr = new Promise((resolve, reject) => {
setTimeout(()=>resolve("Hello"), 1000)
})
pr.then(console.log)
위 코드에서 pr이라는 이름의 Promise는 1000ms 후에 fulfilled 상태로 변하게되고, 따라서 then 메서드가 호출되면서 then의 콜백이 실행되고, 콜백함수에는 fulfilled된 promise의 value가 인자로 전달된다.
const pr = new Promise((resolve, reject) => {
setTimeout(()=>reject("Something went wrong"), 1000)
})
pr.then(console.log, console.error)
위 코드에서는 pr은 reject 되었기에, 두번째 콜백함수가 실행되게 된다.
하지만 reject된 Promise를 처리하기에 더 쉬운 방법은 catch 메서드를 사용하는 것이다. catch 메서드는 promise가 reject 되었을때만 실행되며, then 에서 에러를 처리하는 것 보다 가독성이 더 좋으며, then의 callback에서 발생한 에러도 잡아낼 수 있기에 catch를 사용해서 처리하는것이 권장된다.
const pr = new Promise((resolve, reject) => {
setTimeout(() => reject("Something went wrong"), 1000)
})
pr.then(console.log).catch(console.error);
2. Async-Await
Promise도 근본적으로 then 을 이용해서 callback을 깊어지지 않게 만들어졌다는 것 뿐, 콜백패턴을 사용하고 있단 것은 변하지 않았다. 이런 상황에서 실제 동작은 비동기적으로 이루어지지만, 코드의 흐름은 동기적으로 보이도록해서 개발자가 흐름을 파악하기 쉽게 만드는 방법을 고민하게 되었고 그 결과 Async-Await 문법이 탄생했다.
Async-Await는 Promise를 기반으로 동작한다. 따라서 Promise에 대한 이해없이는 제대로 사용하기 어렵다. 자바스크립트에서는 async 함수를 선언할 수 있으며 함수 앞에 async라는 키워드를 붙여서 선언한다.
async function some(){}
async 함수의 리턴값은 항상 Promise이다. 함수 내부에서 Promise를 return하지 않더라도, 모든 return value를 암묵적으로 Promise로 감싸서 내보내는 동작을 수행한다.
그리고 async 함수 내부에서는 await 키워드를 사용할 수 있다.
async function getData(){
const response = await fetchSomeData();
const data = await response.json();
return data
}
await는 Promise가 settled 될 때까지 함수의 실행을 잠시 멈추고 대기해준다. 그 후 Promise가 settled된다면 함수를 계속 실행시켜준다. 이런 동작을 통해서 마치 async 함수 내의 코드의 흐름을 동기적으로 생각하면서 작성할 수 있기에 개발자가 함수의 흐름을 파악하기 쉬워진다는 장점이 있다.
또한 async 함수에서는 await 중인 promise가 reject된 경우에는 이를 예외로 던져준다. 따라서 async 함수에서는 try, catch 블록을 이용해서 발생한 예외를 처리할 수 있게 된다.
async function getData(){
try {
const response = await fetchSomeData();
const data = await response.json();
return data
} catch (e) {
console.error(e)
}
}
3. Promise 고급 활용
1) Promise.all()
- 인자로 Promise의 배열을 받는다. resolve된 value들의 배열을 가진 Promise를 리턴
- 인자로 받은 Promise 중 하나라도 reject 된다면, 동작을 중지하며 reject된 Promise를 리턴
const delay = (ms) => {
return new Promise((resolve) => {
setTimeout(() => resolve(`completed in ${ms}`), ms);
});
};
async function withoutPromiseAll() {
await delay(1000);
await delay(1000);
await delay(1000);
}
async function withPromiseAll() {
await Promise.all([delay(1000), delay(1000), delay(1000)]);
}
2) Promise.allSettled()
- Promise.all의 동작과 유사하고 단, reject된 promise가 있더라도 전체를 모두 처리해서 배열에 담아준다.
- 배열 안의 요소들은 각기 status와 value 또는 reason 프로퍼티를 가진 객체
const pr1 = Promise.resolve(1);
const pr2 = Promise.reject("rejected");
Promise.allSettled([pr1, pr2]).then(console.log)
/*
[
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: 'rejected' }
]
*/
3) Promise.race()
- 인자로 Promise의 배열을 받고, 그 중 가장 먼저 resolve되거나 reject된 promise를 반환한다.
const pr1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const pr2 = new Promise((resolve) => setTimeout(() => resolve(2), 50));
Promise.race([pr1, pr2]).then(console.log);
'Language > JavaScript' 카테고리의 다른 글
[JavaScript] 비동기를 구현하는 방법 (Event loop & Callback) (0) | 2022.11.09 |
---|---|
비동기에 대해서 (0) | 2022.11.08 |
Iterator & Generator (0) | 2022.10.29 |
Javascript ES6 전체적으로 살펴보기 (0) | 2022.06.27 |
[JavaScript] 유사 배열 객체(Array-like objects) (0) | 2022.03.23 |