티스토리 뷰
Promise
한국어로 약속
이라는 뜻이다.
Promise는 자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 객체이다.
Promise는 정해진 기간의 기능을 수행 한 후에 정상적으로 기능을 수행했다면 성공의 메세지와 함께 결과 값을 전달해주고 만약 기능을 수행하는 데에 문제가 생기면 에러를 전달 해준다.
Promise에는 두가지의 포인트가 있다.
- state : promise의 상태
- producer와 consumer의 차이
Promise의 상태는 다음과 같이 나뉜다
pending -> fulfilled or rejected
또 promise를 만드는 producer
와 이것을 사용하는 comsumer
로 나뉜다.
콜백 대신 Promise를 사용하는 코드예시를 살펴보자.
const promise = new Promise((resolve, reject) =>{
// 비동기 작업 수행
console.log('doing something')
})
Promise는 위와 같이 만들 수 있다.
콘솔을 살펴보면 알겠지만 promise를 만듬과 동시에 해당 작업이 수행되는 것을 볼 수 있다.
따라서 promise를 만들때 불필요한 작업이 바로 수행되지 않도록 유의해야 한다.
resolve
이번엔 비동기 작업을 실제로 수행하는 코드를 짜보자.
const promise = new Promise((resolve, reject) => {
// 비동기 작업 수행
setTimeout(() => {
const data = 'some data'
resolve(data)
}, 1000)
})
이 작업은 promise내에서 setTimeout함수를 통해 1초 뒤 data를 전달 해 주는 코드이다.
setTimeout이 정상적으로 수행이 되고나면 callback이 불릴테고, 그때 resolve를 통해 데이터를 전달 해 주는 것이다.
이제 이 promise를 consumer
입장에서 사용 해 보자.
promise.then((val) => console.log(val))
promise가 완료되면 데이터를 받아 콘솔로 찍어주는 코드이다.
콘솔을 확인해보면 정상적으로 1초뒤에 some data
가 출력되는 것을 볼 수 있다.
reject
그렇다면 reject는 언제 사용해야 할까?
reject는 우리가 어떠한 작업을 할 때 작업에 실패하여 에러를 발생해야 할 때 쓰인다.
const promise = new Promise((resolve, reject) => {
// 비동기 작업 수행
setTimeout(() => {
reject(new Error('Error'))
}, 1000)
})
setTimeout을 수행하던 중 오류가 났다고 가정했다.
reject에는 Error객체를 보낸다. 이 의미는 promise의 작업이 실패했다는 것이다.
이 오류는 아래 코드처럼 catch로 잡아줄 수 있다.
promise
.then((val) => console.log(val))
.catch((err) => {
console.log(err)
})
위 코드가 가능한 이유는 then역시 promise를 return하게 되고 promise에 있는 catch를 이용한 것이다.
Promise chaining
promise를 통해 얻은 결과를 chaining을 통해 다음 작업에도 계속 쓸 수 있다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10)
}, 1000)
})
promise
.then((value) => value * 2)
.then((value) => value * 2)
.then(
(value) =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value / 2)
}, 1000)
})
)
.then((value) => {
console.log(value - 1)
})
- 먼저 1초뒤에 10을 받아오는 promise를 만들었다.
- 이제 그 promise가 완료되면 결과에 2를 곱하여 promise를 반환한다.
- 이어서 또 2를 곱하여 promise를 반환한다.
- 여기서 비동기작업이 또 필요하다고 가정하여 1초뒤에 결과에 2를 나누는 promise를 반환한다.
- 마지막으로 promise를 받아 1를 뺀 결과를 console하고 연결은 끝이난다.
chaining을 이용한 코드를 한번 보자.
const getEgg = () =>
new Promise((resolve, reject) => {
setTimeout(resolve('🥚'), 1000)
})
const getChick = (egg) =>
new Promise((resolve, reject) => {
setTimeout(resolve(`${egg} => 🐣`), 1000)
})
const getChicken = (chick) =>
new Promise((resolve, reject) => {
setTimeout(resolve(`${chick} => 🐤`), 1000)
})
getEgg()
.then(getChick)
.then(getChicken)
.then((chicken) => {
console.log(chicken)
})
promise를 이용해 비동기작업을 하여 병아리의 알부터 시작하여 병아리로 부화하는 과정이다.
최종적으로 마지막 then에 도달하면 콘솔에는 🥚 => 🐣 => 🐤
의 결과가 나올것이다.
그런데 만약 중간에 오류가 생겼다면 어떨까?
🥚 => 🐣
이 과정에서 오류를 내보았다. reject(new Error(ERROR! ${egg} => 🐣))
그리고 마지막 then이후에 catch를 통해 오류를 잡아보았다.
getEgg()
.then(getChick)
.then(getChicken)
.then((chicken) => {
console.log(chicken)
})
.catch((err) => console.log(err))
다음과 같은 에러가 나왔다. Error: ERROR! 🥚 => 🐣
그런데 만약에 중간에 에러가 났을 때 그 값을 대체하여 다른 값을 전달해 마지막 함수까지 실행하고 싶을 때는 어떻게 해야 할까?
먼저 reject에게 인자로 받은 data와 error를 전달해 주었다.
reject({ data: egg, error: new Error('error') })
그리고 catch문에서 새로운 값을 return 하여주면 된다.
getEgg()
.then(getChick)
.catch(({ data, error }) => {
console.log(error)
return `${data} => 🐥 `
})
.then(getChicken)
.then((chicken) => {
console.log(chicken)
})
.catch((err) => console.log(err))
이렇게 하여 코드를 돌려보았을 때 🥚 => 🐥 => 🐤
의 결과를 얻을 수 있었다.
async와 await
promise를 조금 더 간결하고 간편하고 마치 동기적으로 실행되는 것 처럼 보여주는 기능이다.
promise에는 chaining을 할 수 있다. chaining이 길어진다면 아무리 콜백지옥만큼은 아니여도 코드가 난잡해 질 수 있다.
async와 await은 promise위에서 조금 더 간편하게 사용할 수 있는 api이다.
한가지 기억해야 할 점은 무조건 promise대신 async와 await을 사용할 필요는 없다는 점이다.
promise를 유지하여 써야 하는 경우가 있을 수 있다.
async를 붙여 함수를 만들게 되면 우리가 내부적으로 promise를 생성하지 않아도 자동으로 promise를 생성하여 return 해준다.
const promise = new Promise((resolve, reject) => {
// 비동기 작업 수행
setTimeout(() => {
resolve('done')
}, 1000)
})
promise.then(console.log)
따라서 async를 사용하면 위와 같은 코드를
async function promise() {
// 시간이 걸리는 작업
return 'done'
}
const p = promise()
p.then(console.log)
이렇게 바꿔줄 수 있다.
await은 async가 붙은 function 안에서 사용할 수 있다.
then을 사용하는 것과 같은 개념이지만 마치 동기적으로 작동하는 것 같은 코드를 작성할 수 있다.
하지만 한 함수내에서 await을 연달아 사용한다면 병렬처리를 하지 못할 수 있다.
따라서 병렬처리가 필요한 작업에는 별도의 함수를 만들어 병렬처리하고 그 결과를 await을 통해 받아오거나 promise.all이나 promise.race등 promise의 api를 사용하는 것이 좋다.
그럼 위에 promise에서 사용했던 예제를 async와 await을 사용해 바꾸어 보겠다.
const getEgg = () =>
new Promise((resolve, reject) => {
setTimeout(resolve('🥚'), 1000)
})
const getChick = (egg) =>
new Promise((resolve, reject) => {
setTimeout(resolve(`${egg} => 🐣`), 1000)
})
const getChicken = (chick) =>
new Promise((resolve, reject) => {
setTimeout(resolve(`${chick} => 🐤`), 1000)
})
getEgg()
.then(getChick)
.then(getChicken)
.then((chicken) => {
console.log(chicken)
})
이 코드를
// 1초뒤 결과를 얻는 것을 구현하기 위한 dealy 함수
const delay = (ms) => new Promise((resolve) => setTimeout(() => resolve(), ms))
async function getEgg() {
await delay(1000)
return '🥚'
}
async function getChick(egg) {
await delay(1000)
return `${egg} => 🐣`
}
async function getChicken(chick) {
await delay(1000)
return `${chick} => 🐤`
}
async function getResult() {
try {
const egg = await getEgg()
const chick = await getChick(egg)
const chicken = await getChicken(chick)
console.log(chicken)
} catch (err) {
console.log(err)
}
}
getResult()
이렇게 마치 동기적으로 동작하는 코드 처럼 변경해 줄 수 있다.
'Javascript' 카테고리의 다른 글
[JS] 자바스크립트 비동기 예외처리(Promise, async, await) (0) | 2021.12.24 |
---|---|
[JS] 자바스크립트 예외처리 (0) | 2021.12.24 |
[JS] 자바스크립트 함수형 접근(functional approach) (0) | 2021.07.13 |
[JS] 자바스크립트 변수선언, scope, hoisting (0) | 2021.07.13 |
[JS] 자바스크립트의 비동기 처리, 이벤트 루프 (0) | 2021.07.13 |
- Total
- Today
- Yesterday
- nodeJS
- 백준
- 백트래킹
- 세그먼트 트리
- 재귀
- 예외처리
- 동적계획법
- dfs
- 자바
- java
- node.js
- typeORM
- 벨만포드
- Computer Architecture
- 컴퓨터 통신
- nestjs
- BFS
- 자바스크립트
- 그리디
- ReactNative
- 스레드
- nest.js
- 알고리즘
- 시뮬레이션
- boj
- 컴퓨터 구조
- 투포인터
- 중앙대학교
- 구현
- 그래프
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |