javascript

非同期処理に用いるPromiseとasync/awaitとは

非同期処理のそもそもの活用シーンとPromiseパターンとasync/awaitパターンの実装について解説しました

非同期処理の活用シーン

次のようなスクリプトを例に考えます。"ああああ"という実行結果が得られます。

function getResult() {
  return "ああああ"
}

let result = getResult()
console.log(result)  //実行結果: ああああ

しかし上の例のように瞬時に結果を得られるというケースばかりではなく、実際には複雑な計算が必要だったり通信によるタイムラグが発生します。

そしてgetResult()の実行結果を得るのに10秒かかってしまったケースをコードで再現すると次のようになります。

function getResult() {
  sleep(1000) //10秒待機
  return "ああああ"
}

let result = getResult()
console.log(result)  //nil

この例では"ああああ"ではなく、nilという実行結果が得られるはずです。これはreturn "ああああ"が実行される前にconsole.log(result)が実行されてしまうことが理由です。

このようにして起こるタイムラグの問題をスマートに解決するために非同期処理が使われます。

非同期処理の実装パターン2種の解説

非同期処理を行う方法の2種類とその特徴を簡単に述べると次のようになります

  1. Promiseパターン

    元祖非同期処理。どこでも利用できますが、次のasync/awaitに比べ、複雑で見慣れないコードになるのがデメリット。

  2. async/await パターン

    通常の同期処理のような書き方でシンプルに非同期処理を実装できる。ただし、非同期メソッド(async function)のスコープ内でのみ利用可能

従って非同期処理を行いたい場合、async/awaitを使えるときはまずこちら優先し、それ以外の場合はpromiseを用いると良いかと思います。

・Promiseパターン

そもそも非同期処理について端的に説明するとこうなります。Qiitaの記事から引用です。

「ある関数が呼び出されたとき、戻り値として本来渡したい結果を返すのではなく、一度関数としては終了し(=呼び出し元に戻る)、後で『本来渡したかった値』を返せる状態になったときに、呼び出し元にその値を通知する」という仕組み

出典: 非同期処理ってどういうこと?JavaScriptで一から学ぶ

この挙動をPromiseオブジェクトというものによって次のように実現します。

  1. Promiseオブジェクトの定義

    Promiseオブジェクトは二つの値を引数に取ります。Promiseオブジェクトの生成が正常に行われるとresolve()が呼ばれ、reject()が呼ばれます

    const promise = new Promise((resolve, reject) => {
     resolve(getResult());
    });
  2. コールバックの記述

    続いてコールバック関数の記述を行います。コールバック関数とは先ほど作成したpromiseオブジェクトのresolveが完了したタイミングで実行される関数です。具体的には.thenメソッドが呼ばれます。以下のようにするとgetResult()の実行結果が得られた、タイミングでその結果がログ出力されることになります。

    const promise = new Promise((resolve, reject) => {
     resolve(getResult());
    });
    
    promise.then((result) => {
     console.log(result)
    });

・Async/Awaitパターン

こちらは非同期メソッド内でしか使用できないという制約がありますが、それでも必要なのは次の2つだけです。

  1. 非同期関数(async function)の生成

    非同期関数を定義するには宣言の前にasyncをつけます。簡単。

    async function helloAsync() {
    
    }
  2. awaitで非同期処理

    任意の処理を行う際awaitをつけることで非同期処理として実行されます。うん、簡単。

    async function helloAsync() {
     let result = await getResult()
     console.log(result)
    }

使い分け

非同期処理の実装が必要な場合、非同期関数(async function)が使えるなら同期処理的な記述で非同期処理できるAsync/Awaitパターンを利用すれば、特に苦労はないかと思います。もし非同期関数が使えない場合は諦めてPromiseを利用しましょう。ただし使いこなすにあたっては原理をある程度理解しておく必要があります。

※エラーハンドリングについて追記予定。

おまけ: 同期処理・非同期処理・並列処理の違い

通常の同期処理、今回取り扱った非同期処理に加えて並列処理というものが存在します。3つの処理を簡単にまとめるとこんな感じです

  • 同期処理: 書いた順に実行
  • 非同期処理: 時間がかかる処理は待って実行
  • 並列処理: 複数のタスクを同時に進行

読んでいただきありがとうございます!!

記事として取り上げたトピックを体系化してまとめた内容を電子書籍として販売しています。購入していただくことで執筆の応援ができます。詳細はこちらから