JavaScript

【JavaScript】非同期処理(async await)

今回は前回に引き続き、非同期処理について書いていきたいと思います。今回はPromiseのシンタックスシュガーであるasync awaitです。これを使うと簡単に非同期処理を制御することができ、現在のJavaScriptで必須の知識になっているのでしっかり見ていきます。

thenを使って書いてみる

まずは、thenで受け止める書き方について見ていきます。今回もサンプルコードはりあクト!から拝借しています。

import fetch from 'node-fetch';

const getUser = async (userId) => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)

  if (!response.ok) {
    throw new Error(`${response.status} Error`);
  } else {
    return response.json();
  }
}

console.log('-- Start --');

getUser(2)
  .then((user) => {
    console.log(user);
  })
  .catch((error) => {
    console.log(error);
  })
  .finally(() => {
    console.log('-- Completed --');
  });

さっそくasync awaitが使われていますが、一旦この部分は気にしないことにします。重要なのはgetUser()を実行することで、Promiseオブジェクト(以下Promise)が変えるということです。それでは、詳しく追っていきます。

まず、4行目ではAPIを叩いて、データを得ます。ここでは、Responseオブジェクトが返り変数responseに代入されます。

const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)

Responseオブジェクトは色々なプロパティやメソッドを持っており、その中のひとつのResponse.okというプロパティを使います。これは、ステータスコードが200ならtrueを返し、それ以外ならfalseを返すというものです。

少しややこしいですが、!が使われているので、ステータスコード200が返ると9行目が実行されます。これもResponseオブジェクトが持つメソッドのひとつで、取得したデータをPromiseオブジェクトで返してくれます。

return response.json(); // ユーザデータがPromiseオブジェクトで返る

そのため、15行目のgetUser(2)の返り値はPromiseになります。Promiseが返っているので、then()で受け止めることができます。thenの引数のuserには先程returnしたユーザのデータが入っているので、これを実行すると、次のようになります。

$ node get-user-then.js 
-- Start --
{
  id: 2,
  name: 'Ervin Howell',
  username: 'Antonette',
  email: 'Shanna@melissa.tv',
  address: {
    street: 'Victor Plains',
    suite: 'Suite 879',
    city: 'Wisokyburgh',
    zipcode: '90566-7771',
    geo: { lat: '-43.9509', lng: '-34.4618' }
  },
  phone: '010-692-6593 x09125',
  website: 'anastasia.net',
  company: {
    name: 'Deckow-Crist',
    catchPhrase: 'Proactive didactic contingency',
    bs: 'synergize scalable supply-chains'
  }
}
-- Completed --

ここで、何度も登場したResponseオブジェクトの詳細はMDNのページに詳細が乗っています。一度目を通しておくといいかと思います!

async awaitを使う場合

さて、次にasync awaitを使うとどのように書けるのかを見ていきます。

import fetch from 'node-fetch';

const getUser = async (userId) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users/${userId}`,
  );

  if (!response.ok) {
    throw new Error(`${response.status} Error`);
  }

  return response.json();
};

console.log('-- Start --');

const main = async () => {
  try {
    const user = await getUser(2);
    console.log(user);
  } catch (error) {
    console.error(error);
  } finally {
    console.log('-- Completed --');
  }
};

main();

17行目までは先程と同じです。17行目にはasyncという何やら見慣れないものがでてきました。これはこの先説明するawaitを使うために必要なものです。ルールとして引数の前にasyncを書きます。

const main = async () => {   // 非同期処理をawaitで制御する場合は、asyncをつける

ここで、userという変数に取得したユーザ情報を代入しているのですが、その前にawaitがついています。日本語で「待つ」という意味の通り非同期処理でありながら、getUser(2)が完了するまで、次の処理に移るのを待ちます。非同期処理は通常、並列で実行が可能なのでユーザの取得に遅れようものなら、待たずに次へ進んでしまいます。すると、userがundefinedになるので、途中でエラーになります。それを防ぐためthenを使って制御しているのでした。それをawaitと書くだけで、userという変数に値が入っていることが担保されるわけです。すごく便利ですね!

実行してみると問題なくユーザが取得できていることが確認できると思います。

$ node get-user-await.js 
-- Start --
{
  id: 2,
  name: 'Ervin Howell',
  username: 'Antonette',
  email: 'Shanna@melissa.tv',
  address: {
    street: 'Victor Plains',
    suite: 'Suite 879',
    city: 'Wisokyburgh',
    zipcode: '90566-7771',
    geo: { lat: '-43.9509', lng: '-34.4618' }
  },
  phone: '010-692-6593 x09125',
  website: 'anastasia.net',
  company: {
    name: 'Deckow-Crist',
    catchPhrase: 'Proactive didactic contingency',
    bs: 'synergize scalable supply-chains'
  }
}
-- Completed --

もぐくん
もぐくん
awaitの方が書く量が少なくて済むし、直感的だね!

さいごに

今回はReactでの開発でもよく使われているasync awaitについて見てきました。何となく使うこともできるけど、基本であるPromiseの理解をしておくのはやっぱり大事だと感じました。(何より気持ち悪い…)
ここまで読んでいただきありがとうございました。

ABOUT ME
sakai
三重出身の28歳。前職はメーカーで働いていて、プログラミングスクールに通って未経験からWeb業界に転職しました。Railsをメインで使っていて、AWSも少しできます。音楽を聞くこととYoutubeを見るのが好きです。最近はへきトラ劇場にハマってます