async/await地獄を避けるにはどうすればいいですか?

3645 ワード

コメント:async/awaitはさわやかに書いてありますが、これらの問題に注意してください.
async/awaitは私たちを地獄の転調から解放しましたが、これはまたasync/await地獄の問題を導入しました.
何がasync/await地獄ですか?
Javascriptで非同期プログラミングをする時、人々はいつもawait文をたくさん使います.多くの場合、私達の文は前の語句に依存しなくてもいいです.そうすると、性能に問題があります.
async/await地獄の例
ピザと飲み物を買うプログラムを書いてみます.
(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
})()
このコードの運行には問題がありません.しかし、それは良い実装されていないので、待つ必要はありません.
説明
私達はすでに私達のコードを非同期IIIIFに封入しました.次の順序で実行します.
ピザリストをもらって飲み物のリストを取って、リストの中からピザを選んでください.選択したピザをカートに入れて、選択した飲み物をカートに入れてショッピングカートの中の物品を注文します.
問題
ここで問題がありますが、なぜリストからピザを選ぶ動作は飲み物リストの取得を待つ必要がありますか?この二つは関連のない操作です.関連操作は2つのグループがあります.
ピザのリストを取得します.-』ピザを選んでカートに入れます.
飲み物のリストを取得します.-』飲み物を選んで、カートに入れます.
この二つの操作は同時に実行されるべきです.
もっと悪い例を見てください.
このJavascriptコードのセグメントはショッピングカートの中の商品を注文します.
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キーワードを忘れたらどうなりますか?
非同期関数を呼び出してawaitを忘れた場合、この機能を実行するのは待つ必要がないということです.非同期関数は直接プロミスに戻ります.後で使えます.
(async () => {
  const value = doSomeAsyncTask()
  console.log(value) // an unresolved promise
})()
あるいはプログラムがよく分かりません.関数の実行が終わるまで待ってください.この非同期タスクは直接終了しません.ですから、私たちはawaitというキーワードを使う必要があります.
promiseには興味深い属性があります.あなたはある行のコードからpromiseを取得し、他の場所でそれを待つことができます.これはasync/await地獄を解決する鍵です.
(async () => {
  const promise = doSomeAsyncTask()
  const value = await promise
  console.log(value) // the actual value
})()
あなたが見たように、doSomeAsyncTaskは直接Promiseに戻ります.この非同期関数doSomeAryncTaskはすでに実行を開始しました.doSomeAcyncTaskの戻り値を得るために、awaitが必要です.
どうやってasync/await地獄を避けるべきですか?
まず、どの名前が前後の依存関係にあるかを知る必要があります.その後、依存関係のある一連の動作をグループ化して非同期的な動作にする.これらの非同期関数を同時に実行します.この例を書き直します.
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
})()
私たちは文を二つの関数に分けます.関数の内部では、各ステートメントは前のステートメントの実行に依存します.この二つの関数を同時に実行します.
第二の例では未知数のPromiseを処理する必要がある.この問題を処理するのは非常に簡単です.私達はただ一つの配列を作成して、全部のPromiseを中に入れて、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
}