[번역] 비동기 지옥(async hell) 탈출방법

in #wdev6 years ago

바쁘신 분을 위한 짧은 요약

promise.all 을 활용하여 그룹화 된 promise 수행을 통해 async 함수를 효과적으로 빠르게 수행합니다.

출처 : https://medium.freecodecamp.org/avoiding-the-async-await-hell-c77a0fb71c4c

비동기(async)는 콜백지옥(callback hell)에서 우리를 자유롭게했습니다. 하지만 이는 잘못된 사용으로 인해 비동기 지옥(async hell)의 탄생으로 이어졌습니다.
이 글에서는 async / await 이 무엇인지 설명하며, 비동기 지옥 탈출을위한 몇 가지 팁을 공유합니다.

async / await 지옥이란 무엇입니까?

async 자바스크립트로 작업하는 동안 사람들은 여러 문장을 차례로 작성하고 함수 호출 전에 기다리고 있습니다. 이로 인해 하나의 명령문이 이전 명령문에 의존하지 않아도, 이전 명령문이 완료 될 때까지 기다려야합니다.

async / await hell의 예

피자와 술 주문을 위한 코딩을 생각해보십시오.


(async () => {
  const pizzaData = await getPizzaData()    // async call
  const drinkData = await getDrinkData()    // async call
  const chosenPizza = choosePizza()    // sync call
  const chosenDrink = chooseDrink()    // sync call
  await addPizzaToCart(chosenPizza)    // async call
  await addDrinkToCart(chosenDrink)    // async call
  orderItems()    // async call
})()

겉으로 보기에는 정확하고 잘 동작합니다. 하지만 실제론 좋은 구현은 아닙니다. 이제 문제를 인식하고 해결해 보도록 합시다.

설명

비동기 함수를 즉시 실행함수로 래핑했습니다. 이는 아래 순서로 발생합니다.

1 피자 목록 획득.
2 음료 목록을 획득.
3 목록에서 피자 선택.
4 목록에서 음료 선택.
5 선택한 피자를 장바구니 담기.
6 선택한 음료를 장바구니 담기.
7 장바구니 항목 주문.

뭐가 잘못됬나 ?

앞서 강조했듯이, 이 모든 구문은 하나씩 실행되며 여기에는 동시성이 없습니다.

잘 생각해 보세요 : 왜 우리는 음료 목록을 가져오기 전에 피자 목록을 가져오려 기다리는 중입니까? 단, 피자를 선택할 때 피자 목록을 미리 준비해야합니다. 음료수도 마찬가지입니다.

따라서 피자와 관련된 작업이 동시에 진행될 수 있으나, 피자 관련 개별 단계는 순차적으로 (하나씩) 수행되어야합니다.

나쁜 구현의 또 다른 예

아래 코드는 장바구니에있는 항목을 가져 와서 주문을 수행하는 것입니다.

async function orderItems() {
  const items = await getCartItems()    // async call
  const noOfItems = items.length
  for(var i = 0; i < noOfItems; i++) {
    await sendRequest(items[i])    // async call
  }
}

이 경우 for 루프는 다음 반복을 계속하기 전에 sendRequest() 함수가 완료 될 때까지 대기해야합니다. 그러나 실제로 기다릴 필요는 없습니다. 모든 요청을 가능한 한 빨리 보내고 모든 요청이 완료 될 때까지 기다릴 수 있습니다.

async / await 지옥이 무엇인지 이제 감이 오시나요 ?

await 키워드가 사라진다면 ?

비동기(async) 함수를 호출하는 동안 기다리는 것(await)을 빼면 함수가 바로 실행되기 시작합니다. 즉, 함수를 실행하는 데 대기 시간이 필요하지 않습니다. 비동기(async) 함수는 나중에 사용할 수있는 약속(Promise)을 반환합니다.

또 다른 결과는 컴파일러가 함수가 완전히 실행될 때까지 기다리고 싶다는 것을 알지 못한다는 것입니다. 따라서 컴파일러는 비동기 작업을 끝내지 않고 프로그램을 종료합니다. 그래서 우리는 await 키워드가 필요합니다.

약속(Promise)의 한 가지 재미있는 속성은 한 줄에 약속(Promise)을하고 다른 약속(Promise)을 해결할 때까지 기다릴 수 있다는 것입니다. 이것은 async / await hell을 피하는 열쇠입니다.

(async () => {
  const promise = doSomeAsyncTask()
  const value = await promise
  console.log(value) // the actual value
})()

보시다시피 doSomeAsyncTask()가 약속(Promise)을 반환합니다. 이 시점에서 doSomeAsyncTask()가 실행을 시작했습니다. 약속의 해결 된 값을 얻기위해 await 키워드를 사용하면 JavaScript가 다음 줄을 즉시 실행하지 않고 결과를 확인한 후 실행하라는 약속(Promise)을 기다립니다.

async/await 지옥에서 빠쪄나오는 방법

async/await 을 피하기 위해 다음 단계를 따라야합니다.

첫 번째 예에서는 피자와 음료를 선택했습니다. 우리는 피자를 선택하기 전에 피자 목록을 가지고 있어야한다고 결론을 내렸고 피자를 장바구니에 넣기 전에 피자를 선택해야합니다. 그래서 우리는이 세 단계가 서로 의존한다고 말할 수 있습니다. 우리는 이전 일을 마칠 때까지 한 가지를 할 수 없습니다.

그러나 좀 더 광범위하게 살펴보면 피자 선택은 음료 선택에 달려 있지 않기 때문에 병행으로 선택할 수 있음을 알게됩니다.

따라서 우리는 다른 명령문의 실행에 의존하는 명령문그렇지 않은 명령문을 알아야 됩니다.

비동기 함수의 그룹 종속 문

위에서와 같이 피자를 선택하는 것은 피자 목록을 얻고 그중 하나를 선택하고 난 이후 피자를 장바구니에 추가하는 것과 같은 종속적 문장을 포함합니다. 우리는 이러한 명령문을 비동기 함수로 그룹화해야합니다. 이렇게하면 두 개의 비동기 함수 인 selectPizza () 및 selectDrink ()가 발생합니다.

이러한 비동기 함수를 동시에 실행하세요

이후 비동기(비차단)함수를 동시에 실행하기 위해 이벤트 루프를 이용합니다.이는 Promise.all 함수를 통해 처리할 수 있습니다.

예제 수정하기

위 단계를 거쳐 예제에 적용 해 보겠습니다.

async function selectPizza() {
  const pizzaData = await getPizzaData()    // async call
  const chosenPizza = choosePizza()    // sync call
  await addPizzaToCart(chosenPizza)    // async call
}

async function selectDrink() {
  const drinkData = await getDrinkData()    // async call
  const chosenDrink = chooseDrink()    // sync call
  await addDrinkToCart(chosenDrink)    // async call
}

(async () => {
  const pizzaPromise = selectPizza()
  const drinkPromise = selectDrink()
  await pizzaPromise
  await drinkPromise
  orderItems()    // async call
})()

// Although I prefer it this way 

(async () => {
  Promise.all([selectPizza(), selectDrink()].then(orderItems)   // async call
})()

이제 우리는 두 문장을 두 가지 기능으로 그룹화했습니다.

함수 내에서 각 구문은 이전 구문의 실행에 의존합니다. 그런 다음 selectPizza() 및 selectDrink() 함수를 동시에 실행합니다.

두 번째 예에서는 알 수없는 약속을 처리해야합니다.

이 상황을 다루는 것은 매우 쉽습니다. 배열을 만들고 약속을 푸시합니다. 그런 다음 Promise.all()을 사용하여 우리는 모든 약속이 동시에 해결 될 때까지 기다립니다.

async function orderItems() {
  const items = await getCartItems()    // async call
  const noOfItems = items.length
  const promises = []
  for(var i = 0; i < noOfItems; i++) {
    const orderPromise = sendRequest(items[i])    // async call
    promises.push(orderPromise)    // sync call
  }
  await Promise.all(promises)    // async call
}

이 글을 통해 async / await의 기본을 이해하고, 이후 응용 프로그램의 성능을 향상시키는 데 도움이되기를 바랍니다.

Sort:  

좀 더 알기 쉽게 예를 들면...

1 피자 목록 획득.
2 음료 목록을 획득.
3 목록에서 피자 선택.
4 목록에서 음료 선택.
5 선택한 피자를 장바구니 담기.
6 선택한 음료를 장바구니 담기.
7 장바구니 항목 주문.

각 항목당 수행 시간이 1초식 걸린다면 ...

기존 비동기에서는 7초가 걸리지만...

그룹화된 비동기를 활용하면 (3,3,1) => 4초면 해결됨 !

Coin Marketplace

STEEM 0.30
TRX 0.12
JST 0.033
BTC 63898.89
ETH 3129.16
USDT 1.00
SBD 3.90