6 minute read

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는 두 번째 인자로 옵션을 줄 수 있다.

Categories:

Updated: