[Javascript] 비동기

youngseo·2022년 5월 2일
0

Javascript

목록 보기
43/46
post-custom-banner

비동기

비동기 처리는 네트워크 통신에서만 해당하는 내용이 아니라 특정 로직에서도 충분히 사용될 수 있습니다. 그렇기 때문에 비동기 처리를 이해하는 것은 매우 중요합니다.

1. 비동기 처리 방식

1-1 콜백함수 이용

function getMovie(cb){
  fetch(API_URL)
    .then(res => res.json())
    .then(res => {
      cb(res)
  })
}

getMovie(movies => {
  console.log(movies)
})

하지만 콜백함수를 이용한 비동기 처리 방식의 경우 콜백지옥에 빠질 수도 있어, 이런 경우 작성하기도 어려워지지만 해석하기도 관리하기도 어렵다는 매우 큰 단점이 있습니다.

1-2 Promise 이용

이러한 콜백지옥을 개선하기 위해 나온 것이 바로 Promise 클래스 입니다.

function getMovie() {
  //promise instance가 반환되게 됩니다.
  return new Promise((resolve) => {
    fetch(API_URL)
      .then(res => res.json())
      .then(res => {
        resolve(res)
    })
  })
}

getMovie().then(res => console.log(res))

자바스크립트에서 기본적으로 내장되어 제공되는 fetch 함수처럼 getMovies라는 함수를 만들어 호출하고 그 결과를 then으로 받아 사용하는 구조를 만들었습니다.

언제 끝날지 알 수 없는 영화정보를 가져오는 로직을 getMovie로 호출한 후 비동기코드 then을 이용해 로직이 끝나면, 그 때 결과를 콜백안에서 사용하겠다는 작동원리입니다.

1-3 async await 이용

getMove() 코드 실행 결과 Promise Instance를 반환하기 때문에 .then(res => console.log(res)) 대신에 async await를 사용해 표현할 수 있습니다.

즉, getMovie().then(res => console.log(res))const res = await getMovie()의 코드 실행 결과는 같습니다.

하지만 asyncawait가 포함된 함수로 감싸줘야하기 때문에 아래와 같이 정리할 수 있습니다.

function getMovie() {
  //promise instance가 반환되게 됩니다.
  return new Promise((resolve) => {
    fetch(API_URL)
      .then(res => res.json())
      .then(res => {
        resolve(res)
    })
  })
}

;(async function () {
  const res= await getMovie()
  console.log(res)
})()

2. 예외처리

특히 비동기코드에서 예외상황이란 언제든지 발생할 수가 있습니다. 따라서 예외처리를 해주어야합니다.

2-1 try {} catch(err) {} finally {}

aysnc, await을 사용하는 경우에는 try, catch문을 사용해 예외처리를 할 수 있습니다.

async function getMovie() {
  try {
    let res = await fetch(API_URL)
    res = await res.json()
    return res
  } catch (error) {
    alert('에러가 발생했어요')
  } finally {
    console.log('실행완료')
    //ex) loading 애니메니션 종료
  }
}

2-2 then().catch().fianlly()

then()을 사용하는 경우에는 try, catch문을 사용하지 않고 아래와 같이 catch메소드와 finally메소드를 통해 작성할 수 있습니다.

function getMovie() {
  fetch(API_URL)
    .then(res => res.json())
    .then(res => res)
    .catch(error=> alert('에러발생'))
    .finally(() => {console.log(error)})
}

하지만 async, awiat를 사용하는 방식이 조금 더 권장되고 있기 떄문에 실제로는 try,catch문을 쓸 일이 조금 더 많을 수 있습니다.

3. 요청이 두번 발생한 경우

3-1 요청을 두번 발생한 경우

function getMovie() {
  fetch(API_URL)  //실행후 
    .then(res => res.json()) //여기가 아니라
    .then(res => {
      console.log('fetch1', res)
    })

    fetch(API_URL) //여기가 실행 
    .then(res => res.json())
    .then(res => {
      console.log('fetch2', res)
    })
}

getMovie()

위와 같이 요청이 두 번 발생한 경우, API_URL요청이 거의 동시에 출발하기 떄문에 누가 먼저 도착할지는 예측할 수 없습니다.

3-2 순서 보장

만약, 발생 순서를 보장해주고 싶은 경우라면 아래와 같이 코드를 작성할 수 있습니다.

function getMovie() {
  fetch(API_URL)
    .then(res => res.json())
    .then(res => {
      console.log('fetch1', res)

      fetch(API_URL)
      .then(res => res.json())
      .then(res => {
        console.log('fetch2', res)
      })
    })
}

getMovie()

하지만 위 코드의 경우 콜백지옥과 비슷하다고 할 수 있습니다.

3-2 Promise.all 사용

위와 같은 코드를 보완하기 위해 Promise.all 개념을 사용할 수 있습니다. Promise.all을 이용하면 비동기 통신이 모두 완료될 때까지 기다릴 수 있습니다.

1번 : 한번에 처리(직렬)

function getMovie() {
  Promise.all([  //실제로 통신하는 두코드를 배열로 묶습니다.
    fetch(API_URL)
      .then(res => res.json()),  
    fetch(API_URL2)
      .then(res => res.json())    
  ])
    .then( res => {//res는 배열로 객체구조분해로 사용할 수도 있습니다.
      console.log('fetch1:', res[0])
      console.log('fetch2:', res[1])
      console.log('Done!')
    })
}

async, awiat 패턴으로 표현하는 경우 아래와 같습니다.

2번 :병렬

async function getMovie() {
  const res1= await fetch(API_URL)
      .then(res => res.json()) 
  const res2 = await fetch(API_URL2)
      .then(res => res.json())


  console.log('async1:', res1)
  console.log('async2:', res2)
  console.log('Done!')
}

getMovie()

하지만 위 코드에는 문제가 있습니다. 바로, 첫번째 데이터가 도착을 해야지만 두번째 요청이 들어간다는 것입니다.

만약 데이터가 오는데 10초가 걸린다고 가정을 할 때 1번 코드는 10초전후의 시간이 걸리지만, 2번 코드의 경우 20초가 넘게 걸리게 되는 것입니다.

마찬가지로 Promise.all을 사용해 아래와 같이 수정하는 경우 마찬가지로 동시에 데이터를 처리할 수 있습니다.

async, await문에서의 Promise.all사용

async function getMovie() {
  const [res1, res2] = await Promise.all([
    fetch(API_URL)
      .then(res => res.json()),  
    fetch(API_URL2)
      .then(res => res.json())
  ])

  console.log('async1:', res1)
  console.log('async2:', res2)
  console.log('Done!')
}

getMovie()
post-custom-banner

0개의 댓글