비동기(Asynchronous) : Promise 와 async & await

이동환·2020년 9월 22일
1

TIL

목록 보기
31/74

Asynchronous

: 비동기 호출. 비동기 호출이란 동기 호출이랑 반대되는 개념이다. 동기는 순차적으로 호출되고 실행되어지지만, 비동기는 그렇지않다. 자바 스크립트를 배울때 비동기는 우리가 필요할때 실행시키는것을 비동기 호출이라고 부른다.

일상적인 예로는 커피를 주문할때와 같다.

동기는 한 손님이 커피를 주문하고, 그 주문한 커피가 나올때까지 그 손님이 비키지 않는다. 그러면 그 뒤에 있는 손님들은 계속 기다려야만 한다. 우리는 이것을 blocking되었다고 말한다.

그러나 비동기는 모든 손님들이 음료가 나올때까지 기다릴 필요없이, 자기가 원하는 음료를 주문하면된다. 그러면 주문하는 도중에 음료가 다 만들어지고, 음료를 뺄 수 있다. 이렇게 주문을하고, 완료되면 음료를 받아 갈 수 있는것을 우리는 unblocking이라고 부른다.

실생활에서 예제를 봤다면, 우리가 사용하는 컴퓨터 세계에서는 애니메이션, 네트워크와 연결 , 파일을 불러올때, 백그라운드 실행, 로딩 창 등과 같은곳에서 사용되어진다.

Callback

위의 비동기 호출을 하기 위해서 우리가 할 수 있는 방법중 첫번째는 콜백이다.

let callbackFunc = (string, callback) => {
  setTimeout( () => {
    console.log(string)
    callback()
  },3000)
}
let printStr = () => {
  callbackFunc('hi',()=> {
    callbackFunc('hello', () =>{
      callbackFunc('world',() =>{})
    })
  })
}
printStr()
// 'hi'
// 'hello'
// 'world'

3초마다 하나씩 출력되는것을 볼 수 있다. 그러나 콜백을 10번을 실행한다면 ?

let printStr = () => {
  callbackFunc('hi',()=> {
    callbackFunc('hello', () =>{
      callbackFunc('world',() =>{
        callbackFunc('4times',() =>{
          callbackFunc('5times',() => {
            callbackFunc('6times',() => {
              callbackFunc('7times',() => {
                .........
              })
            })
          })
        })
      })
    })
  })
}

이처럼 가독성이 좋지않지않은 코드를 보게된다. 우리는 이것을 '콜백지옥' 또는 'callback - hell'이라고 부른다.

그래서 우리는 대안으로 promise를 사용한다.

Promise와 async&await를 공부하기전에 알아야 할 것

  • 모든 비동기는 Promise를 리턴한다.

Promise

프로미스는 하나의 인스턴스와 같다. 우리가 인스턴스를 만들때 new를 사용한거처럼 프로미스도 new를 사용해야한다. 또 프로미스는 인자로 함수를 받는데, 그 함수는 resolve와 reject라는 함수 인자를 받는다.

아래의 코드는 비동기인 프로미스를 리턴한는 함수를 나타내는 코드다.

let promise = (string) =>{
  return new Promise((resolve, reject) => {
    setTimeout(
      () => {
        console.log(string)
        resolve()
      }, 3000)
  })
}
//
let printStr = () =>{
  promise('hi')
    .then(()=>{
      return promise('hello')
    })
    .then(() => {
      return promise('word')
    })
}
printStr();

콜백을 사용할때와, 프로미스를 사용할때를 비교해보자.

이렇게 Promise와 then 메소드를 사용하여 좀 더 간략하게 표현 할 수 있다.

MDN에서 promise를 더 정확히 알아보면, (promise() 사용법)

Promise는 프로미스가 생성될 때 꼭 알 수 있지는 않은 값을 위한 대리자로, 비동기 연산이 종료된 이후의 결과값이나 실패 이유를 처리하기 위한 처리기를 연결할 수 있도록 합니다. 프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하지는 않고, 대신 프로미스를 반환해서 미래의 어떤 시점에 결과를 제공합니다.

다시말해, Promise 객체는 비동기 호출 후 미래에 fulfilled 또는 reject의 값을 나타냅니다.

Promise는 다음 중 하나의 상태를 가집니다.

대기(pending): 이행하거나 거부되지 않은 초기 상태.
이행(fulfilled or resolve): 연산이 성공적으로 완료됨.
거부(rejected): 연산이 실패함.

*추가
settled : fulfilled 상태이든 reject 상태이든 결론이 난 상태를 말함.

  • 알아두면 좋은 Promise 메소드

Promise.all(iterable)
iterable 내의 모든 프로미스가 이행한 뒤 이행하고, 어떤 프로미스가 거부하면 즉시 거부하는 프로미스를 반환합니다. 반환된 프로미스가 이행하는 경우 iterable 내의 프로미스가 결정한 값을 모은 배열이 이행 값입니다. 반환된 프로미스가 거부하는 경우 iterable 내의 거부한 프로미스의 이유를 그대로 사용합니다. 이 메서드는 여러 프로미스의 결과를 모을 때 유용합니다

function getNewsAndWeatherAll() {
  let news = fetch(newsURL);
  let weather = fetch(weatherURL);
//
  return Promise.all([news, weather])
    .then(resArr => resArr.map(data => data.json()))
    .then(jsonArr => Promise.all(jsonArr))
    .then(res => {
      return { news: res[0].data, weather: res[1] };
    });
}

then()
: then은 프로미스 인스턴스가 리턴하는 값을 인자로 받으면서, 그 프로미스가 실행된 후에 바로 실행되어지는 메소드이다. 주로 resolve의 값을 받으면 가끔 에러값을 반환하기도한다(?).

catch()
: 이 메소드는 주로 마지막에 사용되어진다. 프로미스 인스턴스가 then메소드를 모두 실행하고, 에러값이 뜨면 에러를 내보내기위해서 catch메소드를 사용한다.

  • 주의할 점

프로미스도 콜백 지옥처럼 프로미스 지옥을 만들 수 있다.

그러나 이걸 잘 해결할 수 있는 방법은 return 을 잘 활용하면 해결 할 수 있다.

아래 처럼 새로 실행 시켜야할 함수를 실행 시키고, then메소드를 사용하면 된다.

async 와 await

: async와 await는 JS의 비동기 처리 방법 중 하나로 promise의 단점을 보완하고, 가독성이 더 좋게 만들어 줄 수 있다.

이 문법을 사용할때 주의해야 할 점.

  1. 어떤 한 function 앞에 async 를 적어서 이 함수는 비동기 함수라는것을 명시 해줘야한다.
  2. async 가 사용된 함수에서 내장함수(비동기로 처리할 함수)에 await를 사용하여 비동기적으로 처리한다.

    await는 JS가 Promise가 settled 되기 전까지 기다리게 하는 역할을 한다.

  • 여기서도 주의점. async와 await 는 promise 인스턴스에만 사용이 가능하다.

이렇게 async 와 await를 사용하면, 동기적으로 사용한것처럼 코드의 가독성을 높일 수 있다. 아래의 그림이 예시다.

async & await에서 예외 처리하기

: async & await에서 예외(오류)를 처리하기 위해서는 try catch 문법을 사용하면된다.

async function foo(){
  try{  
    if(false){
      const test = await randomFunc();
      console.log(test);
    }
  } catch(err){
    console.log(err);
  }
}

참고하면 좋은 사이트
1. https://javascript.info/async-await
2. https://developers.google.com/web/fundamentals/primers/async-functions
3. https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff (advanced)

profile
UX를 개선하는것을 즐기고 새로운것을 배우는것을 좋아하는 개발자입니다.

0개의 댓글