JavaScript PromiseとAsync/Awaitの詳細な説明

JavaScript PromiseとAsync/Awaitの詳細な説明

概要

一般的に、開発では、ネットワーク API 操作のクエリには時間がかかることが多く、応答を待つのに時間がかかる場合があります。そのため、要求されたときにプログラムが応答しない状況を回避するために、非同期プログラミングは開発者の基本的なスキルになります。

JavaScript で非同期操作を扱うとき、「Promise」という概念をよく耳にします。しかし、それがどのように機能し、どのように使用するかを理解するのは抽象的で難しい場合があります。

4つの例

さて、この記事では、その概念と使用法をより早く理解できるように実用的な方法を使用します。そのため、多くの従来の無味乾燥なチュートリアルとは異なり、次の 4 つの例から始めます。

  • 例1: 誕生日を使ってPromiseの基本を説明する
  • 例2: 数字当てゲーム
  • 例3: Web APIから国情報を取得する
  • 例4: Web APIから国に隣接する国のリストを取得する

例1: 誕生日で説明する約束の基本

まず、Promise の基本形式を見てみましょう。

Promise が実行されると、保留中 (実行中)、完了 (成功)、拒否 (失敗) の 3 つの状態に分けられます。

新しいPromise(function(resolve,reject) {
    if (/* 非同期操作が成功しました*/) {
        resolve(value); // Promiseの状態をパディングから実行済みに変更する
    } それ以外 {
        拒否(エラー); // Promise の状態をパディングから拒否に変更します
    }
})
実装には3つのプロトタイプメソッドがあります: then、catch、finally
約束
.then((結果) => {
    //promise が受け入れられるか拒否されて実行が続行されます})
.catch((エラー) => {
    //promise は拒否されます})
.finally (() => {
    //promise が満たされると、とにかく実行されます})

基本的な形式を紹介したので、次の例を見てみましょう。

ユーザーストーリー: 友人の Kayo が、2 週間後の私の誕生日パーティーでケーキを焼いてくれると約束してくれました。

すべてがうまくいって、カヨが病気でなければ、私たちはある程度の量のケーキを手に入れることができますが、カヨが病気であれば、ケーキはもらえません。しかし、ケーキがあってもなくても、私たちは誕生日パーティーをします。

この例では、上記の背景を JS コードに変換してみましょう。まず、Promise を返す関数を作成しましょう。

定数 onMyBirthday = (isKayoSick) => {
  新しい Promise を返します ((resolve, reject) => {
    タイムアウトを設定する(() => {
      if (!isKayoSick) {
        解決する(2);
      } それ以外 {
        拒否(新しいエラー("悲しいです"));
      }
    }, 2000);
  });
};

JavaScript では、new Promise() を使用して新しい Promise を作成できます。これは、パラメータ (resolve, reject) => {} を持つ関数を受け入れます。

この関数では、resolve と deny はデフォルトで提供されるコールバック関数です。上記のコードを詳しく見てみましょう。

2000 ミリ秒後に onMyBirthday 関数を実行します。

  • カヨが病気でない場合は、パラメータとして2を指定してresolve関数を実行します。
  • Kayo が病気の場合は、引数として new Error("I am sad") を使用して、reject を実行します。拒否したいものを引数として渡すことはできますが、Error オブジェクトを渡すことをお勧めします。

ここで、onMyBirthday() は Promise を返すため、then、catch、finally メソッドにアクセスできるようになります。 then と catch で、先に resolve と reject に渡された引数にアクセスすることもできます。

次のコードを通して概念を理解しましょう

加代が病気でなかったら

私の誕生日(false)
  .then((結果) => {
    console.log(`I have ${result} cakes`); // コンソールに「I have 2 cakes」と表示される 
  })
  .catch((エラー) => {
    console.log(error); // 実行されませんでした})
  .finally(() => {
    console.log("Party"); // コンソールに「Party」と表示される
  });

加代が病気になったら

私の誕生日に(true)
  .then((結果) => {
    console.log(`I have ${result} cakes`); // 実行されませんでした})
  .catch((エラー) => {
    console.log(error); // コンソールに「I'm sad」と表示される
  })
  .finally(() => {
    console.log("Party"); // コンソールに「Party」と表示される
  });

この例を通して、Promise の基本的な概念を理解できると思います。

例2: 数字当てゲーム

基本要件:

  • ユーザーは任意の数字を入力できる
  • システムは1から6までの数字をランダムに生成します
  • ユーザーが入力した数字がシステムの乱数と等しい場合、ユーザーには2ポイントが与えられる。
  • ユーザーが入力した数字がシステムの乱数と1異なる場合、ユーザーには1ポイントが与えられ、そうでない場合は0ポイントが与えられます。
  • ユーザーは好きなだけプレイできる

上記の要件を満たすために、まず enterNumber 関数を作成し、Promise を返します。

const 入力番号 = () => {
  新しい Promise を返します ((resolve, reject) => {
    // ここからコーディングを開始します });
};

まず、ユーザーに数字を尋ね、1 から 6 までの数字をランダムに選択します。

const 入力番号 = () => {
  新しい Promise を返します ((resolve, reject) => {
    const userNumber = Number(window.prompt("数字を入力してください (1 - 6):")); // ユーザーに数字を尋ねます const randomNumber = Math.floor(Math.random() * 6 + 1); // 1 から 6 までのランダムな数字を選択します });
};

ユーザーが数値以外の値を入力した場合。この場合、reject 関数を呼び出してエラーをスローします。

const 入力番号 = () => {
  新しい Promise を返します ((resolve, reject) => {
    const userNumber = Number(window.prompt("数字を入力してください (1 - 6):")); // ユーザーに数字を尋ねます const randomNumber = Math.floor(Math.random() * 6 + 1); // 1 から 6 までのランダムな数字を選択します if (isNaN(userNumber)) {
      拒否(new Error("入力タイプが正しくありません")); // ユーザーが入力した値が数値でない場合は例外がスローされ、拒否関数が呼び出されます}
  });
};

次に、userNumber が RanomNumber と等しいかどうかを確認する必要があります。等しい場合は、ユーザーに 2 ポイントを与え、オブジェクト { points: 2, randomNumber } を渡して、resolve 関数を実行できます。

userNumber が randomNumber と 1 異なる場合、ユーザーに 1 ポイントが与えられます。それ以外の場合、ユーザーには 0 ポイントが与えられます。

新しい Promise を返します ((resolve, reject) => {
  const userNumber = Number(window.prompt("数字を入力してください (1 - 6):")); // ユーザーに数字を尋ねます const randomNumber = Math.floor(Math.random() * 6 + 1); // 1 から 6 までのランダムな数字を選択します if (isNaN(userNumber)) {
    拒否(new Error("入力タイプが正しくありません")); // ユーザーが入力した値が数値でない場合は例外がスローされ、拒否関数が呼び出されます}
 
  if (ユーザー番号 === ランダム番号) {
    // 等しい場合は、ユーザーに 2 ポイントを与えます。resolve({
      ポイント: 2,
      ランダム番号、
    });
  } それ以外の場合 (
    ユーザー番号 === ランダム番号 - 1 ||
    ユーザー番号 === ランダム番号 + 1
  ){
    // userNumber と randomNumber の差が 1 の場合、ユーザーに 1 ポイントを与えます。resolve({
      ポイント: 1,
      ランダム番号、
    });
  } それ以外 {
    // それ以外の場合、ユーザーは 0 ポイントを獲得します。resolve({
      ポイント: 0,
      ランダム番号、
    });
  }
});

次に、ユーザーにゲームを続行するかどうかを尋ねる別の関数を作成しましょう。

const 継続ゲーム = () => {
  新しいPromise((resolve) => {を返す
    if (window.confirm("続行しますか?")) { // ユーザーにゲームを続行するかどうかを尋ねます。resolve(true);
    } それ以外 {
      解決(偽);
    }
  });
};

ゲームが強制終了するのを防ぐために、作成した Promise では Reject コールバックを使用しません。

次に、推測ロジックを処理する関数を作成します。

const ハンドル推測 = () => {
  enterNumber() // Promiseオブジェクトを返します。then((result) => {
      alert(`Dice: ${result.randomNumber}: you got ${result.points} points`); // 解決が実行されると、ユーザーのスコアと乱数を取得します // ユーザーにゲームを続行するかどうかを尋ねます continueGame().then((result) => {
        if (結果) {
          handleGuess(); // はいの場合、ゲームは続行されます } else {
          alert("ゲーム終了"); // いいえの場合はゲーム終了ボックスを表示します}
      });
    })
    .catch((エラー) => alert(エラー));
};
 
handleGuess(); // handleGuess関数を実行する

ここで、handleGuess 関数を呼び出すと、enterNumber() は Promise オブジェクトを返します。

Promise ステータスが解決された場合は、then メソッドを呼び出して推測結果とスコアをユーザーに通知し、ゲームを続行するかどうかをユーザーに尋ねます。

Promise ステータスが拒否された場合、入力が間違っていたというメッセージをユーザーに対して表示します。

しかし、このようなコードは問題を解決できますが、まだ読みにくいです。後で async/await を使用するように hanldeGuess をリファクタリングしましょう。

async/await については、すでにインターネット上に多くの説明があります。ここでは、シンプルかつ一般的な方法で説明したいと思います。async/await は、複雑で理解しにくい非同期コードを同期のような構文に変換できる構文糖です。

リファクタリングされたコードを見てみましょう。

const handleGuess = 非同期() => {
  試す {
    const result = await enterNumber(); // then メソッドの代わりに、結果を直接取得するには promise の前に await を置く必要があります。alert(`Dice: ${result.randomNumber}: you got ${result.points} points`);
 
    const isContinuing = continueGame() を待機します。
 
    継続中の場合
      ハンドル推測();
    } それ以外 {
      alert("ゲーム終了");
    }
  } catch (error) { // catch メソッドは try、catch 関数 alert(error); に置き換えることができます。
  }
};

関数の前に async キーワードを使用することで、非同期関数を作成します。関数内の使用方法は、次のように以前とは異なります。

  • then 関数とは異なり、結果を直接取得するには、Promise の前に await キーワードを置くだけで済みます。
  • try、catch 構文を使用して、promise の catch メソッドを置き換えることができます。

参考までに、リファクタリング後の完全なコードを以下に示します。

const 入力番号 = () => {
  新しい Promise を返します ((resolve, reject) => {
    const userNumber = Number(window.prompt("数字を入力してください (1 - 6):")); // ユーザーに数字を尋ねます const randomNumber = Math.floor(Math.random() * 6 + 1); // システムは 1 から 6 までの数字をランダムに選択します if (isNaN(userNumber)) {
      拒否(new Error("入力タイプが間違っています")); // ユーザーが数字以外の数字を入力した場合、エラーが発生します}
 
    if (userNumber === randomNumber) { // ユーザーが数字を正しく推測した場合、ユーザーに2ポイントを与えます。resolve({
        ポイント: 2,
        ランダム番号、
      });
    } それ以外の場合 (
      ユーザー番号 === ランダム番号 - 1 ||
      ユーザー番号 === ランダム番号 + 1
    ) { // userNumber が randomNumber と 1 異なる場合、ユーザーに 1 ポイントを与えます。resolve({
        ポイント: 1,
        ランダム番号、
      });
    } else { // 不正解、スコア0 解決({
        ポイント: 0,
        ランダム番号、
      });
    }
  });
};
 
const 継続ゲーム = () => {
  新しいPromise((resolve) => {を返す
    if (window.confirm("続行しますか?")) { // ユーザーにゲームを続行するかどうかを尋ねます。resolve(true);
    } それ以外 {
      解決(偽);
    }
  });
};
 
const handleGuess = 非同期() => {
  試す {
    const result = await enterNumber(); // await は then 関数を置き換えます alert(`Dice: ${result.randomNumber}: you got ${result.points} points`);
 
    const isContinuing = continueGame() を待機します。
 
    継続中の場合
      ハンドル推測();
    } それ以外 {
      alert("ゲーム終了");
    }
  } catch (error) { // catch メソッドは try、catch 関数 alert(error); に置き換えることができます。
  }
};
 
handleGuess(); // handleGuess関数を実行する

2 番目の例が完了したので、3 番目の例に進みましょう。

例3: Web APIから国情報を取得する

多くの場合、API からデータを取得するときに、開発者は Promise を活用します。 https://restcountries.eu/rest/v2/alpha/cn を新しいウィンドウで開くと、国データが JSON 形式で表示されます。

Fetch API を使用すると、データを簡単に取得できます。コードは次のとおりです。

const fetchData = 非同期() => {
  const res = await fetch("https://restcountries.eu/rest/v2/alpha/cn"); // fetch() は Promise を返すので、それを待つ必要があります
 
  const country = await res.json(); // res は HTTP レスポンスのみなので、res.json() を呼び出す必要があります。
 
  console.log(country); // 中国のデータは開発コンソールに記録されます
};
 
フェッチデータ();

必要な国のデータが揃ったので、最後のタスクに進みましょう。

例4: Web APIから国に隣接する国のリストを取得する

次のfetchCountry関数は、例3のAPIから国情報を取得します。パラメータalpha3Codeは、国の国コードです。次のコードです。

// タスク 4: 中国の近隣諸国に関する情報を取得する const fetchCountry = async (alpha3Code) => {
  試す {
    const res = フェッチを待つ(
      `https://restcountries.eu/rest/v2/alpha/${alpha3Code}`
    );
 
    const データ = res.json() を待機します。
 
    データを返します。
  } キャッチ(エラー){
    コンソール.log(エラー);
  }
};

cn を alpha3code として渡して中国の情報を取得する fetchCountryAndNeighbors 関数を作成しましょう。

const fetchCountryAndNeighbors = 非同期 () => {
  const 中国 = fetchCountry("cn") を待機します。
 
  console.log(中国);
};
 
国と近隣地域を取得します。

コンソールでオブジェクトの内容を確認します。

オブジェクトには、中国の近隣諸国の alpha3code のリストである border プロパティがあります。

さて、隣国の情報を次のように取得してみます。

const neighbors =china.borders.map((border) => fetchCountry(border));

neighbors は Promise オブジェクトの配列です。

Promise の配列を扱うときは、Promise.all を使用する必要があります。

const fetchCountryAndNeigbors = 非同期 () => {
  const 中国 = fetchCountry("cn") を待機します。
 
  const neighbors = await Promise.all(
    china.borders.map((border) => fetchCountry(border))
  );
 
  console.log(近隣);
};
 
国と地域の情報をフェッチします。

コンソールでは、 Country オブジェクトのリストが表示されるはずです。

参考までに、例 4 のすべてのコードを次に示します。

const fetchCountry = async (alpha3Code) => {
  試す {
    const res = フェッチを待つ(
      `https://restcountries.eu/rest/v2/alpha/${alpha3Code}`
    );
    const データ = res.json() を待機します。
    データを返します。
  } キャッチ(エラー){
    コンソール.log(エラー);
  }
};
 
const fetchCountryAndNeigbors = 非同期 () => {
  const 中国 = fetchCountry("cn") を待機します。
  const neighbors = await Promise.all(
    china.borders.map((border) => fetchCountry(border))
  );
  console.log(近隣);
};
 
国と地域の情報をフェッチします。

要約する

これら 4 つの例を実行すると、非同期操作、つまり同時に発生しない操作を処理するときに Promise が便利であることがわかります。継続的に練習することで、理解が深まり、強くなると信じています。この記事が Promise と Async/Await の理解に役立つことを願っています。

この記事で使用されているコードは次のとおりです: https://files.cnblogs.com/files/powertoolsteam/Promise-Async-Await-main.zip

以上がJavaScript PromiseとAsync/Awaitの詳しい説明です。JavaScript PromiseとAsync/Awaitについてさらに詳しく知りたい方は、123WORDPRESS.COMの関連記事もぜひご覧ください!

以下もご興味があるかもしれません:
  • async/await と promise (Node.js における非同期操作の問題)
  • JavaScriptのPromiseを徹底的に理解する
  • Javascript の async 関数の詳細な説明
  • JavaScript の async と await のシンプルで詳細な学習
  • Javascript における promise、async、await の違いの詳細な説明

<<:  Linux で Nginx ロード バランシングを使用して複数の Tomcat を構成する方法

>>:  MySQL5.6.17データベースをインストールするときにMy.iniファイルを構成する方法

推薦する

MySQL InnoDBエンジンのインデックスとストレージ構造の詳細な説明

序文Oracle や SQL Server などのデータベースには、ストレージ エンジンが 1 つだ...

非常に実用的なTomcat起動スクリプトの実装方法

序文セキュリティ上の理由から、会社が Linux サーバーへのすべてのログインにセキュリティ制限を課...

Linux で cmake を使用して MySQL をコンパイルおよびインストールするための詳細なチュートリアル

1. cmakeをインストールする1. cmakeの圧縮パッケージを解凍する [root@mysql...

Dockerイメージを構築する2つの方法

目次既存のイメージからイメージを更新します。イメージを最初から構築する: Docker イメージ リ...

JSはプログレスバーをドラッグして要素の透明度を変更することを実装しています

今日ご紹介したいのは、ネイティブ JS を使用してプログレス バーをドラッグし、要素の透明度を変更す...

node.js グローバル変数の具体的な使用法

グローバルオブジェクトすべてのモジュールは呼び出すことができますglobal: ブラウザの wind...

Nexus サーバーを設定するための詳細な手順

1. ネクサスサービス構築の意義イントラネットの統合プロキシとして、チームで共同開発する場合、全員が...

WeChatミニプログラムにナビゲーション機能を実装する方法

1. レンダリング2. 操作手順1. テンセントマップキーを申請する - 住所2. ミニプログラムの...

jQueryはバウンドボールゲームを実装します

この記事では、バウンドボールゲームを実装するためのjQueryの具体的なコードを参考までに共有します...

実稼働環境でのNginx高可用性ソリューションの実装プロセスの分析

準備: 192.168.16.128 192.168.16.129 2 台の仮想マシン。 Nginx...

新しいウィンドウで開くジャンプメニュー、window.open の使い方の紹介

コードをコピーコードは次のとおりです。 <前> <div> <sele...

MySQL リンクを表示し、異常なリンクを削除する方法

序文:データベースの運用や保守の際には、リンクの総数がいくつあるか、アクティブなリンクがいくつあるか...

DBeaver を MySQL バージョン 8 以降に接続し、起こりうる問題を解決する方法の詳細な説明

データベース MySQL バージョン 8.0.18 DBeaver.exeをダウンロードするダウンロ...

MySQL 5.7.21 のインストールと設定方法のグラフィックチュートリアル (ウィンドウ)

ウィンドウ環境にmysql5.7.21をインストールします。詳細は次のとおりです。 1. MySQL...