現役エンジニアが教える!実践JavaScript入門 〜非同期処理基本編〜

公開日:2021-07-24
JavaScript
非同期処理

https://youtu.be/4n7-qABcEPI

今回扱う内容:非同期処理

非同期処理の基本を理解しよう。

非同期処理とは

JavaScriptには同期処理と非同期処理があり、非同期処理は時間のかかる処理のことを指します。具体的な使用場面としては次の3つが挙げられ、その中でも1番よく使われるのはAPIリクエストです。

  • APIリクエスト
  • タイマー・イベント
  • ストレージ・ファイルアクセスなど

APIリクエストとはブラウザからサーバにデータを取得する際や、データの登録及び更新をする際のブラウザ・サーバ間でのやり取りの操作のことであり、非同期処理として扱われます。またタイマー・イベント処理では、何秒後・何秒おきに実行する場合や、あるイベント後に実行する場合の処理なども非同期処理として扱われます。他にもサーバサイドのNode.jsなどで使われる、ストレージやファイルアクセスにも非同期処理が使われています。

【例】APIリクエスト(fetch)

fetchメソッドはサーバからデータを取得する際に使うメソッドであり、次のようにURLを引数に記述することで実行できます。今回URLとしてモックのAPIを使用し、テスト用JSONデータを次のように取得します。

const res = fetch("https://jsonplaceholder.typicode.com/todos/1")
console.log(res)

しかし実際に実行すると、返り値はPromiseという型で次のようにデータがラップされてしまい、非同期処理ではシンプルにJSONとして扱えないという問題が生じます。

promise

そのため、JavaScriptの非同期処理で、JSONデータを取得する際には次のように記述する必要があります。

fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then((res) => res.json());
  .then((json) => console.log(json));
console.log("hogehoge")

このようにthenによりメソッドチェインを記述し、返り値を次々と引数に渡すことでJSONに変換したデータを表示できます。
この時の結果は次のようになり、記述した処理の4行目のconsole.log("hogehoge")が先に実行され、3行目が後に実行されていることがわかります。

hogehoge
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

このように非同期処理には処理が書いている順番で実行されないという意味を持ちます。

非同期処理の詳しい内容についてはMDNの記事を参照してみてください。
https://developer.mozilla.org/ja/docs/Learn/JavaScript/Asynchronous/Introducinghttps://developer.mozilla.org/ja/docs/Learn/JavaScript/Asynchronous/Introducing

なぜ非同期に処理を行うのか

非同期に処理を行う理由として次の2つが挙げられます。

JavaScriptは基本的にシングルスレッドで動作するため

非同期処理とは時間のかかる処理を裏側で実行させつつ、完了を待たずに次の処理の実行を進めていき、処理が完了したタイミングでまた別の処理を実行させる、というものです。しかしJavaScriptは基本的にシングルスレッドで動作し、処理単位は1つであるのでJavaScriptにおける非同期処理は別扱いされています。

処理を効率的にこなすため

JavaScriptにおける非同期処理では時間のかかる処理とそうでない処理を分け、扱いを変えることによって処理を無駄なく実行できます。

非同期処理のパターン

非同期処理の書き方としては次の2通りの書き方があります。

  • Promiseを返すパターン(fetchなど)

    ES6以降の新しい書き方であり、最近実装されたメソッドではPromiseを返す書き方が主流となっています。またこちらはAsync/Awaitの書き方にもつながる書き方です。

  • コールバック関数が実行されるパターン(setTimeoutなど)

    ES6以前の書き方であり、従来からあるメソッドではコールバック関数が実行されるこちらの書き方が主流となっています。

Promise と Async/Await を理解する

今回はfetch APIを例にPromiseを使った非同期処理とAsync/Awaitの使い方を学びます。APIのリクエスト先としては、JSONPlaceholderというテスト用のAPIサーバーを利用します。

https://jsonplaceholder.typicode.com/https://jsonplaceholder.typicode.com/

エラーハンドリング

Promiseを使った非同期処理の基本的な記述方法は、APIリクエストの例で示した通りですが、ここではエラーハンドリングについて説明します。
エラーハンドリングとは、プログラムの処理中に何らかのエラーが起きた際、その処理をエラーとして対処する処理のことです。
今回のように非同期処理でのエラーハンドリングを記述するには次のようにcatchメソッドを呼びます。このように記述することで問題なく処理が実行された場合は取得したデータを表示し、何らかのエラーが生じた場合にはerrorのログが表示されます。

fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then((res) => res.json());
  .then((json) => console.log(json));
  .catch((error) => {
    console.log(error);
  });

Async/Awaitの使い方

次にAsync/Awaitについて説明します。この書き方はPromiseのthenでつなげる書き方を同期処理に近い書き方で記述するために生まれた書き方です。使い方としては関数の先頭にasyncをつけ、関数内の非同期処理を呼ぶ際にawaitをつけて次のように記述します。このように書くことでAPIリクエストの例と同じ処理をthende繋げることなく、同期処理のよう直感的に記述できます。

async function main() {
  const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  const json = await res.json();
  console.log(json);
}
main();

Async/Awaitの場合のエラーハンドリング

最後にAsync/Awaitの場合のエラーハンドリングについて説明します。Async/Awaitの場合にはtry...catch文というJavaScriptの構文を用います。
こちらはtry内での処理の中で、何らかのエラーが起きた際にcatch内が実行されるといった同期処理の書き方になっています。

async function main() {
  try {
      const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
      const json = await res.json();
      console.log(json);
  } catch (error){
      console.log(error);
  }
}
main();

PromiseやAsync/Awaitについてさらに詳しく知りたい方はMDNの記事を参照してみてください。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promisehttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_functionhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function

まとめ

今回は非同期処理の基礎編について解説しました。次回はPromiseの詳しい使い方や応用について解説します。