Day21
Promise
- Promise란 비동기 작업을 제어하기 위해 나온 개념으로, callback hell에서 어느정도 벗어날 수 있게 해준다.
- Promise로 정의된 작업끼리는 연결할 수 있으며, 이를 통해 코드의 depth가 크게 증가하지 않음.
- Promise 객체는 만들어지는 그 순간 resolve 또는 reject 함수가 실행된다.
1
2
3
4
| const promise = new Promise((resolve, reject) => {
// promise 내부에서 비동기 상황이 종료될 때, resolve 함수 호출
// promise 내부에서 오류 상황일 때, reject 함수 호출
})
|
- promise에서는 then을 이용해 비동기 작업 이후 실행할 작업을 지정한다.
- 아래 코드에서는 asyncPromiseWork 함수가 끝난 다음에 then안에 지정된 함수가 실행됨. 이때 result값은 resolve로 넘겨준 complete가 됨.
1
2
3
4
5
6
7
8
9
| function asyncPromiseWork() {
// some code...
return new Promise((resolve, reject) => {
// some code...
return resolve(() => console.log('hello'))
})
}
// then의 result에는 resolve를 호출할 때 넘긴 'complete'가 들어 있음
asyncPromiseWork().then(result => result())
|
- Promise의 then 내에서 Promise를 return할 경우 계속 이을 수 있다.
1
2
3
4
5
6
7
8
| promiseWork()
.then(result => {
return promiseNextwork(result)
}).then(result => {
return promiseThridWork(result)
}).then(result => {
return promiseFinalWork(result)
})
|
- Promise chain 중 작업이 실패했을 경우, catch로 잡을 수 있습니다.
- catch를 안 넣을 경우 promise chain중 에러가 발생했을 때 chain이 멈추니 가급적 넣는게 좋음.
1
2
3
4
5
6
7
8
9
10
| promiseWork()
.then(result => {
return promiseNextwork(result)
}).then(result => {
return promiseThridWork(result)
}).then(result => {
return promiseFinalWork(result)
}).catch(e => {
alert('에러 발생!')
})
|
- 성공과 실패의 여부와 상관없이 호출해야하는 코드가 있다면 finally에서 처리함.
1
2
3
4
5
6
7
8
9
10
11
12
| promiseWork()
.then(result => {
return promiseNextwork(result)
}).then(result => {
return promiseThridWork(result)
}).then(result => {
return promiseFinalWork(result)
}).catch(e => {
alert('에러 발생!')
}).finally(() => {
alert('어쨌든 작업 끝')
})
|
- 기존의 callback 함수를 promise 형태로 만들 수 있음.
- resolve 작업이 끝나는 순간에 호출하면 됨.
```javascript
const delay = (delayTime) => new Promise((resolve) => {
setTimeout(resolve, delayTime)
})
delay(5000)
.then(() => {
console.log(new Date().toISOString())
return delay(3000)
}).then(() => {
console.log(new Date().toISOString())
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- Promise 처리된 delay와 조합하기
```javascript
request(`${API_END_POINT}/todos`)
.then(data => {
this.setState({
isLoading: false,
todoList: data,
comments: []
})
return delay(5000)
}).then(() => {
console.log('Complete!')
})
|
Promise 내장 함수들
Promise.all(iterable)
: 여러 개의 promise 객체를 배열로 받아서 모든 객체들이 성공적으로 실행이 완료되면 끝난 걸로 하는 함수, 동시에 모두 실행시켜주기 때문에 비동기 처리이다, 클라이언트에서 API 여러개를 호출할 때 많이 씀.
```javascript
const delay = (delayTime) => new Promise((resolve) => setTimeout(resolve, delayTime))
const promise1 = delay(1000)
const promise2 = delay(2000)
const promise3 = delay(3000)
// 3초 뒤에 work done이 한 번 뜸!
Promise.all([promise1, promise2, promise3]).then((result) => {
console.log(‘work done!’);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
- `Promise.race(iterable)` : 여러 개의 promise 객체 중 <b><u>성공 or 실패 관계없이</u></b> 한 객체만 완료되면 끝난 걸로 하는 함수
```javascript
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
const promises = [1,2,3,4,5].map(n => {
const delayTime = getRandomInt(1000, 5000)
return new Promise(resolve => {
setTimeout(() => {
console.log(`${n}번 고양이 완주`)
resolve(`${n}번 고양이 승리`)
}, delayTime)
})
})
Promise.race(promises).then(message => console.log(message))
|
Promise.any(iterable)
: 여러 개의 promise 객체 중 한 객체가 성공적으로 완료되면 끝난 걸로 하는 함수
```javascript
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
const promises = [1,2,3,4,5].map(n => {
const delayTime = getRandomInt(1000, 5000)
return new Promise((resolve,reject) => {
if(n === 1) {
return reject(${n}번 고양이 기권
)
}
setTimeout(() => {
console.log(${n}번 고양이 완주
)
resolve(${n}번 고양이 승리
)
}, delayTime)
})
})
Promise.any(promises).then(message => console.log(message))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
- `Promise.allSettled(iterable)` : 여러 개의 promise 객체들이 <b><u>성공 or 실패 관계없이</u></b> 모두 이행된 경우 끝난 걸로 하는 함수
- Promise가 실행 중이면 `pending`, 다 끝나면 `setteld`라고 한다.
```javascript
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
const promises = [1,2,3,4,5].map(n => {
const delayTime = getRandomInt(1000, 5000)
return new Promise((resolve,reject) => {
if(n % 2 === 0) {
return reject(`${n}번 고양이 기권`)
}
setTimeout(() => {
resolve(`${n}번 고양이 완주`)
}, delayTime)
})
})
Promise.allSettled(promises).then(message => console.log(message))
|
Promise.resolve
: Promise 객체에서 비동기 처리를 하지 않고 resolve 처리된 Promise.then 객체를 돌려주는 함수
Promise.reject
: Promise 객체에서 비동기 처리를 하지 않고 reject 처리된 Promise.then 객체를 돌려주는 함수
async, await
- Promise가 callback depth를 줄여주긴 하지만 여전히 불편하다.
- 여기서 async, await를 이용하면 Promise를 동기 코드처럼 보이게 짤 수 있다. 하지만 여전히 비동기적으로 실행된다.
```javascript
const delay = (delayTime) => {
return new Promise(resolve => setTimeout(resolve, delayTime))
}
const work = () => {
console.log(‘work run’)
delay(1000)
.then(() => {
console.log(‘work 1 complete.’)
return delay(1000)
})
.then(() => {
console.log(‘work 02 complete.’)
return delay(1000)
})
.then(() => {
console.log(‘work 3 complete.’)
return delay(1000)
}).then(() => {
console.log(‘All work completed!’)
})
console.log(‘work run’)
}
work()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| 이런 코드를 async를 이용하여 짜면
```javascript
const delay = (delayTime) => {
return new Promise(resolve => setTimeout(resolve, delayTime))
}
const work = async () => {
console.log('work start')
await delay(1000) // delay함수가 끝날 때까지 멈춘다.
console.log('work 1 complete.')
await delay(1000)
console.log('work 02 complete.')
await delay(1000)
console.log('work 3 complete.')
await delay(1000)
console.log('work all complete.')
console.log('work complete')
}
work()
|
이렇게 짤 수 있다.
1
2
3
4
5
6
7
| async function asyncFunction () {
const res = await request()
}
const ayncsFunction = async () => {
const res = await request()
}
|
- async를 쓸 때는 기본적으로 함수명이나 () 앞에 붙여서 쓴다.
- await는 async로 선언된 함수 안에서만 쓸 수 있다.
1
2
3
4
5
| async function asyncRun() {
return 'hello'
}
console.log(asyncRun()) // 호출 1
asyncRun().then((message) => console.log(message)) // 호출 02
|
- async가 붙은 함수의 리턴값은 Promise 객체로 감싸진다.
1
2
3
4
5
6
7
8
9
| async function asyncFunction () {
try{
const res = await request()
} catch {
// Promise의 .catch와 비슷한 역할
} finally {
// Promise의 .finally와 비슷한 역할
}
}
|
- async가 붙은 함수에서 에러가 발생할 때는 await가 붙은 변수나 함수를 try-catch문으로 감싸서 처리해준다.
- JS에서 비동기를 관리하는 3가지 방법 :
callback
, Promise
, async, await
fetch API
- 비동기 http 요청을 좀 더 쓰기 편하게 해주는 API
- XMLHTTPRequest를 대체한다.
- Promise 기반으로 동작한다.
1
2
3
4
5
6
7
| fetch(url)
.then(res => {
return res.json() // Promise 객체
})
.then(todos => {
console.log(todos)
})
|
- fetch의 응답 결과는 Response 객체이다.
- Response 객체를 얻은 뒤엔 응답을 json으로 바꾸거나 text로 바꾸는 등의 처리를 해줘야한다.
- blob은 이미지를 처리하는데 쓸 수 있다.
- fetch는 HTTP error가 발생하더라도 reject되지 않는다. 따라서 성공했는지 체크를 해줘야 한다.
- 서버 요청 중에 에러가 생겼을 경우 response 객체의 status code나 ok를 보고 도착했는지 여부를 판단할 수 있음.
1
2
3
4
5
6
7
8
9
10
11
| fetch(url)
.then(res => {
if(res.ok) {
return res.json()
}
throw new Error(`Status : ${res.status} 요청이 처리되지 못함`)
})
.then(todos => {
console.log(todos)
})
.catch(e => alert(e.message))
|
- res.ok는 Status가 200~299 사이인 경우 true가 된다.
1
2
3
4
5
6
7
8
| const headers = new Headers();
headers.append('x-auth-token','TOKEN')
fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(product)
})
|
- fetch는 두 번째 인자로 옵션을 줄 수 있다.