11.JS 비동기 (asynchronous)

Updated:


callback, Promise

callback 함수란..

  • 기존에 동기 방식 (코드가 순서대로 작동하는 방식) - Synchronous code 예시
function a() {
  console.log('A')
}
function b() {
  console.log('B')
}
b()
a()

// 코드 순서대로 B 가 먼저 출력이 되고 그다음에 A가 출력이 됨

  • callback 함수의 개념 : call(호출), back(뒤로, 나중에) 로써 어떤 함수의 로직이 끝난다음에 실행되는 함수를 호출 하는 것이 callback 함수라고 합니다.

// function a 가 1초 뒤에 출력 될 수 있게 설정
function a(callback) { // callback 이라는 매게 변수를 사용해서 A 가 출력된 다음에  B가 출력 될 수 있도록 보장이 됨
  const str = 'Hello A'
  setTimeout(() => {
    console.log('A')
    callback(str)
  }, 1000) 
}
function b() {
  console.log('B')
}
a(function (event) { // callback function a 가 실행된 다음에 b가 실행 됨
  console.log(event)
  b()
})

image

  • 위의 code 처럼 실행순서를 정해서 보장해 나갈 수 있게 됩니다.
// callback 함수 사용 예시
function a(callback) { 
  setTimeout(() => {
    console.log('A')
    callback()
  }, 1000) 
}
function b(callback) { 
  setTimeout(() => {
    console.log('B')
    callback()
  }, 1000) 
}
a(function () {
  b(function () {
    console.log('Done!')
  })
})

Animation

그러나!! callback 이 많아 지면 callback 지옥이라는 문제가 발생!!

// callback 지옥 예시

function a(callback) { 
  setTimeout(() => {
    console.log('A')
    callback()
  }, 1000) 
}
function b(callback) { 
  setTimeout(() => {
    console.log('B')
    callback()
  }, 1000) 
}
function c(callback) { 
  setTimeout(() => {
    console.log('C')
    callback()
  }, 1000) 
}
function D(callback) { 
  setTimeout(() => {
    console.log('D')
    callback()
  }, 1000) 
}
a(function () { // callback 이 계속되서 계속 빠져드는거 callback 지옥임
  b(function () {
    c(function () {
      D(function () {
        console.log('Done!')
      })
    })
  })
})

  • callback 이 다음 순서를 보장하는 장점을 가지고 있지만, 코드가 많아지면 복잡해져서 사용하기가 어렵습니다.

  • 이를 보안하고자 Promise 객체를 사용합니다.


Promise 객체란..

MDN Promise 자세히 보기..

function a() {
  // promise 라는 약속의 객체를 반환
  return new Promise(function (resolve) {
    setTimeout(() => {
      console.log('A')
      resolve('Hello A')
    }, 1000)
  })
}
function b() {
  console.log('B')
}
async function test() {
  const res = await a() // a 의 함수의 호출을 기다린 다음에 b() 가 실행되는 형식 즉, 위의 resolve() 가 호출이 되고 나면 다음 b() 로 넘어간다는 의미 임
  console.log('res:', res)
  b()
}
test()

image

  • new Promise 를 사용하여 위의 callback 지옥 대신에 비동기로 처리해서 같은 로직 만들기

function a() {
  // promise 라는 약속의 객체를 반환
  return new Promise(function (resolve) {
    setTimeout(() => {
      console.log('A')
      resolve('Hello A')
    }, 1000)
  })
}
function b() {
  // promise 라는 약속의 객체를 반환
  return new Promise(function (resolve) {
    setTimeout(() => {
      console.log('B')
      resolve('Hello B')
    }, 1000)
  })
}
function c() {
  // promise 라는 약속의 객체를 반환
  return new Promise(function (resolve) {
    setTimeout(() => {
      console.log('C')
      resolve('Hello C')
    }, 1000)
  })
}
function d() {
  // promise 라는 약속의 객체를 반환
  return new Promise(function (resolve) {
    setTimeout(() => {
      console.log('D')
      resolve('Hello D')
    }, 1000)
  })
}

async function test() {
  const h1 = await a()
  const h2 = await b()
  const h3 =await c()
  const h4 =await d()
  console.log('Done!')
  console.log(h1, h2, h3, h4)
}

test()

Animation2

  • 즉, code 에서 처리시간이 필요한 경우 promise 객체, 비동기를 사용하여 code 를 실행 할 수 있습니다.

예외 처리 (then, catch, finally)

Promise.Prototype

image

  • 비동기 기능으로 async, await 은 ECMAScript 2017(ES8) 부터 나온 기능이고, Promise 의 개념은 ECMAScript 2015 (ES6) 에 나온 것이기 때문에 만약, async, await 을 사용할 수 없는 경우에는 Promise.then(), catch(), finally()을 사용하여 비동기 환경을 설정 할 수 있습니다.

Promise.then()

  • then() 은 다음 실행을 보장해주는 method

  • async, await 이 잘 작동 되지 않은 경우에는 위와 같은 방식으로 then()을 사용하여 비동기 기능을 작동 시킬 수 있습니다.

function a() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}
function b() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('B')
      resolve()
    }, 1000)
  })
}
function c() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('C')
      resolve()
    }, 1000)
  })
}
function d() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('D')
      resolve()
    }, 1000)
  })
}

function test() {
  a()
    .then(() => b())
    .then(() => c())
    .then(() => d())
    .then(() => {
      console.log('Done!')
    })
}
test()

Promise.catch()

  • reject 거부 하는것인데 어떠한 조건에 맞을경우 true 일때, reject 를 사용해서 resolve() 가 더 이상 작동하지 못하도록 하는입니다

  • reject 를 callback 에서 불러서 사용하는 것이 .catch() method 입니다

function a(number) {
  return new Promise((resolve, reject) => {
    if (number > 4) {
      reject()
      return
    }
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}


function test() {
  a(7)
    .then(() => {
      console.log('Resolve!')
    })
    .catch(() => {
      console.log('Reject!')
    })
}
test()

image

Promise.finally()

  • finally() 는 마지막에 무조건 실행되는 callback 을 사용할 때 쓰는 method 입니다.

  • 즉, .then() 이 실행된 다음에 .catch() 가 실행 안되도 마지막에는 .finally() 가 작동이 되고, 반대로 .catch() 가 실행된다음에도 .finally()는 시작되는 구조가 됩니다.

function a(number) {
  return new Promise((resolve, reject) => {
    if (number > 4) {
      reject()
      return
    }
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}


function test() {
  a(7)
    .then(() => {
      console.log('Resolve!')
    })
    .catch(() => {
      console.log('Reject!')
    })
    .finally(() => {
      console.log('Done!')
    })
}
test()

image

async, await 을 사용해서 예외처리 사용하기

  • try {}, catch() {} 를 사용해서 await 구문에 error 가 발생했을 경우 catch 로 넘겨서 처리 할 수 있게 만듭니다.

  • finally {} 도 마찬가지로 try 구문에서 사용할 수 있습니다.

function a(number) {
  return new Promise((resolve, reject) => {
    if (number > 4) {
      reject()
      return
    }
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}

async function test() {
  try {
    await a(8)
    console.log('Resolve!')
  } catch (error) {
    console.log('Reject!')
  } finally {
    console.log('Done!')
  }
}
test()

image

API 비동기 처리 연습

  • API 를 불러올때 axios 같은 package 를 통해서 네트워크를 통해 넘어오는 데이터를 기다려야 될때, 비동기를 사용합니다 => 즉!! API 사용시에는 거의 대부분 비동기 처리를 하여, 데이터가 넘어 올때 까지 기다린 다음에 callback 할 수 있게 code 를 작성 합니다.

OMDB API 를 사용하여 비동기 처리

OMDB API

Axios cdn 연결

  • Promise 에서 코드의 상태

    • 대기(pending): 이행하거나 거부되지 않은 초기 상태.

    • 이행(fullfilled): 연산이 성공적으로 완료됨.

    • 거부(rejected): 연산이 실패함.

// 영화정보를 가져오는 fetchMovies 라는 함수를 따로 만들어서 나중에 코드 재활용 할때도 편하고, data 화 해서 사용할 수 있음
// 추가적인 logic 도 따로 분류해서 처리하기 때문에 용의하게 사용할 수 있음
function fetchMovies(title) {
  // pending 상태
  const omdbApiKey = '7035c60c123' // 만약 잘못된 api 키가 들어 오면 reject 처리 함
  return new Promise(async (resolve, reject) => {
    try {
      const res = await axios.get(`https://omdbapi.com?apikey=${omdbApiKey}&s=${title}`)
      // console.log(res)
      // fullfilled 상태
      resolve(res)
    } catch (error) { // api key 가 틀렸기 때문에 unauthorized error 가 되 401 error 가 됨
      console.log(error.messge)
      // rejected 상태
      reject('Why is not working?')
    }
  })
}

async function test() {
  try {
    const res = await fetchMovies('frozen')
    console.log(res)
  } catch (error) {
    console.log(error)
  }
}
test()

function jobs() {
  fetchMovies('jobs')
    .then(res => console.log(res))
    .catch(error => { console.log(error)})
}
jobs()

Reference

Leave a comment