👨‍💻

【JavaScript】async/awaitの違い

に公開

はじめに

APIやサーバーとデータのやり取りをするとなると必ず出くわすのが非同期処理です。ただ、同期処理というのもありそれぞれがどういう意味で、どう違って、どういう時に使われるのかちゃんと説明できないので、まとめていきます。
まとめてみると、名前は難しいけどやってることはそんなに難しくないことがわかると思います。

結論:処理を順番通りに実行するか、先に次の処理を進めるか

同期処理(Synchronous)とは

同期処理とは、順番通りに処理を実行することです。
1つずつ上から処理を実行していくので、前の処理が終わるまでは次の処理は待ちます。例えば、

console.log("処理1");
console.log("処理2");
console.log("処理3");
// 実行順序:処理1→処理2→処理3

となります。1が終わったら2に進み、2が終わったら3に進むもので、複数の処理を実行する方法と言ったら一番最初に想像するものだと思います。

用途としてはすぐに結果が必要な処理で、Bという処理を実行するにはAという処理の結果を使う場合などです。

非同期処理(Asynchronous)とは

非同期処理とは、処理を待たずに次の処理に進んで実行することです。

console.log("処理1");
setTimeout(() => console.log("処理2"), 2000);
console.log("処理3");
// 実行順序:処理1→処理3→(2秒後に)処理2

この例では、処理2が終わるのを待たずして先に処理3を実行してから処理2が実行されます。

用途としては時間がかかる処理を効率的に扱いたい場合に効果的です。例えばAPI通信をするとき、データをフェッチするまでに少し時間があるのでその間に先に他の処理を実行したほうが効率的です。

非同期処理の使い方

JavaScriptでは、非同期処理を扱う方法敏江Promiseasync/awaitがあります。Promiseとは 「将来完了する処理」を表すオブジェクトです。処理の中身としては同じですが、以下のようにPromiseを使って非同期処理を表現する方法があります。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("データ取得成功!");
    } else {
      reject("データ取得失敗...");
    }
  }, 2000);
});

// then/catchで結果を受け取る
promise
  .then((data) => {
    console.log(data); // "データ取得成功!" が出力
  })
  .catch((error) => {
    console.error(error);
  });

console.log("非同期処理を待たずに次の処理へ");

ただ私は個人的にasync/awaitの方が使いやすくエラー処理も直感的に書けるので、この書き方で具体的な例を示したいと思います。

async/awaitの使い方

ここでは、Supabaseからデータをフェッチしてそれを表示するというプログラムを書きます。

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = 'https://xxxx.supabase.co';
const supabaseKey = 'SUPABSE_ANON_KEY';
cosnt supabase createClient(supabaseUrl, supabaseKey);

async function fetchUsers() {
    try {
        const { data, error } = await supabase
            .from('users')
            .select('*')

        if (error) {
            throw error;
        }

        console.log('取得したユーザー:', data);

    } catch (err) {
        console.error('データ取得エラー:', err.message);
    }
}

コードのポイント解説

  1. Supabaseクライアントの初期化
const supabase = createClient(supabaseUrl, supabaseKey);

まず始めにSupabaseのクライアントを初期化します。

  1. 関数の先頭にasync
async function fetchUsers() {...}

関数の先頭にasyncと書いてあるのが、この関数は非同期処理を行うよって宣言しているようなものです。これを書かないとawaitが使えないので必ずこれは書きます。また、awaitはPromiseの完了を待つキーワードであり、関数自体も常にPromiseを返す関数になることを宣言する必要があるのでasyncを冒頭に書きます。

async/awaitを使う場合は必ずtry/catchを使いましょう。

  1. awaitの意味
const { data, error } = await supabase.from('users').select('*');

upabaseからデータをフェッチするところでawaitが使われています。そもそもsupabase.from('users').select('*')はPromiseを返す非同期処理です。awaitを付けることで、Promiseが解決(成功or失敗)するまで次の処理を待ちます。
簡単に言うと「この処理が終わるまで待って、帰ってきた結果を変数に入れてね」という意味。

  1. { data, error }の意味
{ data, error }

また、なぜフェッチしたデータの格納をこういう風に書いてあるかというと、Supabaseのクエリはオブジェクト形式で結果を返すからです。具体的には

{
    data: [...],
    error: null
}

という形で返してきます。また{}でくくるのは分割代入を意味しており、返ってきたオブジェクトの中から特定のプロパティだけを取り出すということを表しています。普通に書くと

const result = await supabase.from('users').select('*');
const data = result.data;
const error = result.error;

このようになるけど{ data, error}の書き方は短くかけるので便利です

まとめ

  • 同期処理:順番通りに処理する、前の処理の結果が必要な場合に使う
  • 非同期処理:処理を待たずに次に進める、時間のかかる処理やAPI通信に便利
  • async/await:Promiseを簡単に扱える構文で、読みやすくエラー処理もやりやすい

Discussion