Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.
비동기적 처리를 할 때 자바스크립트에서는 콜백 방식으로 처리를 많이 한다.
오래 걸리는 작업을 web api, 노드 api를 통해 맡기고 그 결과가 돌아왔을 때 비로소 어떤 작업을 해야 한다는 것을 콜백으로 명시한다.
👉 그렇게 짜게 되면 Callback Hell 😥 (활모양 형태의 코드)
Callback Hell 문제❓ 가독성 문제, 실수할 가능성 ⬆
💡 그렇기 때문에 promise나 async/await 방식으로 해결 💡
어떤 일이 일어날 때마다 오른쪽으로 쭉쭉 간다❗
Promise는 다음 중 하나의 상태를 가진다.
- 대기(pending): 이행하거나 거부되지 않은 초기 상태.
- 이행(fulfilled): 연산이 성공적으로 완료됨.
- 거부(rejected): 연산이 실패함.
new Promise((resolve, reject) => {
console.log('Inside promise')
resolve('First resolve')
}).then((value) => { // 여기서 value는 resolve한 값! (First resolve)
console.log('Inside first then')
console.log('value', value)
})
❗ 실행 결과 ❗
- Promise는 new Promise라는 생성자를 통해서 만들 수 있다.
- 안에 callback을 넣어주는데, 두 개의 인자를 받는다. 👉 (resolve, reject)
- resolve가 이루어지고 나면 프로미스는 그 다음 단계로 진행 👉 then이라는 함수를 통해서 chainig!!
- resolve한 값을 뒤로 쭉쭉 넘겨줌 ❗
new Promise((resolve, reject) => {
console.log('Inside promise')
reject(new Error('Fist reject'))
console.log('Before resolve')
resolve('First resolve')
console.log('After resolve')
})
.then((value) => {
console.log('Inside first then')
console.log('value', value)
})
.catch((error) => {
console.log('error', error)
})
❗ 실행 결과 ❗
- 여기서 promise는 reject된 값으로 결정 ❗ ( resolve 값이 아닌 reject 값으로 넘겨주는 것)
- 그래서, then은 실행X, catch로 넘어감
- promise 값은 reject 값으로 결정됐지만 실행은 다 일어남! 실행 결과 보면 알 수 있음
- catch에서 error 잡히고 error 출력
- catch는 promise chain 상에서 가장 가까이 있는 error 잡아서 error 내줌
// 비동기코드 setTimeout
new Promise((resolve, reject) => {
console.log('Before timeout')
setTimeout(() => {
resolve(Math.random())
console.log('After resolve')
}, 1000) // 1초 뒤에 어떤 값 결정
})
.then((value) => {
console.log('then 1')
console.log('value', value)
})
.then(() => {
console.log('then 2')
})
.then(() => {
console.log('then 3')
})
// timeout 전에 콜솔로그 찍히고
// settimeout 실행 후 resolve실행
// 그 다음 then chain을 타고서 다음으로 넘어거ㅏㅁ
// promise는 비동기 코드를 순차적으로 쭉 이어지게 쓸 수 있게 만듦
// 비동기 코드가 위에서 아래로 순차적으로 쭉
❗ 실행 결과 ❗
- timeout 전에 'Before timeout' console 찍히고
- setTimeout 실행 후 resolve 실행
- 그 다음 then chain 타고서 다음으로 넘어감
- promise는 비동기 코드를 순차적으로 쭉 이어지게 쓸 수 있게 만듦
// 비동기코드 setTimeout
function returnPromiseForTimeout() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random())
}, 1000) // 1초 뒤에 어떤 값 결정
})
}
// 맨첨엔 setTimeout 갖고 resolve 1초 뒤에
// resolve 뒤에 then chain 탄다!
returnPromiseForTimeout()
.then((value) => {
console.log(value) // then chain에서 return한 값이 다시 promise를 리턴
return returnPromiseForTimeout()
}) // 그럼 다음 then chain에서는 promise가 리턴 됐기 때문에 promise가 resolve 될 때까지 기다린 다음에야 then 실행
.then((value) => {
console.log(value)
return returnPromiseForTimeout
})
.then((value) => {
console.log(value)
return returnPromiseForTimeout
})
.then((value) => {
console.log(value)
return returnPromiseForTimeout
})
returnPromiseForTimeout()
❗ 실행 결과 ❗
// @ts-check
const fs = require('fs')
/**
* @param {string} fileName
*/
function readFileInPromise(fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName, 'utf-8', (error, value) => {
if (error) {
reject(error)
}
resolve(value)
})
})
}
readFileInPromise('.gitignore').then((value) => console.log(value))
fs.promises.readFile('.gitignore', 'utf-8').then((value) => console.log(value))
둘 다 같은 일 일어남
매번 promise로 감싸주지 않아도 node에서는 promise 형태의 api 제공하고 있다.
/**
* @param {number} duration
*/
async function sleep(duration) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(undefined)
}, duration)
})
}
async function main() {
console.log('first')
await sleep(1000)
console.log('second')
await sleep(1000)
console.log('third')
await sleep(1000)
console.log('finish!')
await sleep(1000)
}
main()
- async function은 promise를 돌려주는 함수
- promise를 return하는 함수는 async 함수로 만들 수 있다.
- async function 안에서는 다른 async function await할 수 있다.
// @ts-check
const fs = require('fs')
async function main() {
try {
const result = await fs.promises.readFile('.gitignore', 'utf-8')
console.log(result)
} catch (error) {
console.log('error', error)
}
}
main()