💨

非同期処理とPromise/async awaitについて備忘録

2023/01/15に公開約2,900字

最近は外部のAPIからデータを取得して表示させるみたいな処理を書く機会が多いため、改めてJavascriptの非同期処理について復習していきたいと思います。

非同期処理とは?

jsの処理の流れとして、同期処理と非同期処理があります。

同期処理はjsのコードが上から順番に漏れなく実行されます。
Aの処理が終わったらBの処理を実行する、Bの処理が終わったらCの処理を実行するといった感じです。

一方で非同期処理では、処理が完了していなくても次の処理に進めるようになります。
例えばfetchメソッドは非同期通信でリクエストを発行し、レスポンスを取得できるものですが、レスポンンスが返ってきて処理が完了する前に、次の処理が実行されます。
API通信などは処理が重たく、完了を待っていたら後ろの処理も遅れるため結果的にサイトのスピードが遅くなります。それを防ぐのメリットが非同期処理にはあります。

非同期処理の問題点

例えばAPIからユーザーデータを取得して、そのデータを使って処理を実行するといった場合に非同期処理の問題点が出てきます。
以下のconsoleの出力をご覧ください。

const url = "https://○○○○"; //取得するAPIのurl ※ユーザーデータを想定

//APIからデータを取得し、成功時はユーザーネームを返し、失敗時はnullを返す
const GetUserName = () => {
 fetch(url).then(res=> res.json())
 .then(json => {
   console.log("非同期処理成功")
   return json.name; //※「john」が返ってくると想定
  }).catch(error => {	 
   console.log("非同期処理失敗")
   return null
  })
}

const text = "私の名前は";
const name = GetUserName();
console.log(`${text}${name}です。`);

//======== consoleの結果
//私の名前はundefinedです。
//非同期処理成功

consoleの結果を見ていただくと、「私の名前はundefinedです。」といった表示になってしまっています。
これは、GetUserName関数を実行しデータを取得し終える前に、consoleが発火するので、変数nameにはまだデータが何もないということです。
結果nameがundefinedになってしまっています。

const text = "私の名前は";
const name = GetUserName();
//${name}にデータが届く前に実行されてしまう
console.log(`${text}${name}です。`);

非同期処理の完了を待つ方法

上記のような問題を解決するためには、非同期処理を同期処理的に変える必要があります。
要するに、GetUserName()関数が実行され、userデータがname変数に渡ってきた後に、consoleを実行すればいいのです。
そのための方法としては以下の2種類があります。

  • Promise
  • async await

個人的にはPromiseは使ったことがなく、async awaitがよく使われている印象はありますが、この2つについて簡単に解説していきます。

Promise

const url = "https://○○○○"; //取得するAPIのurl ※ユーザーデータを想定

//APIからデータを取得し、成功時はユーザーネームを返し、失敗時はnullを返す
const GetUserName = () => {
 //Promiseの設定 resolveかrejectが返ってきたら完了
 return new Promise((resolve, reject) => {
  fetch(url).then(res=> res.json())
  .then(json => {
    console.log("非同期処理成功")
    return resolve(json.name); //※「john」が返ってくると想定
   }).catch(error => {	 
    console.log("非同期処理失敗")
    return reject(null);
   })
  })
 })

const text = "私の名前は";
//useNameにはresolveかrejectに入った値が入る
GetUserName().then(name => {
 console.log(`${text}${name}です。`);
})


//======== consoleの結果
//非同期処理成功
//私の名前はjohnです。

Promiseを使った方法では、return new Promiseを追加して、引数としてresoveとrejectを指定します。
resoveには非同期成功時にデータが入り、rejectには非同期失敗時にデータが入ります。
実行時にはGetUserName().thenというように繋ぎ、thenの引数nameには、resoveかrejectの値が入るため、consoleのnameにはきちんと値が入ってきている訳です。

async await

async awaitも仕組みは同じで書き方が変わるだけです。

const GetUserName = async () => {

 const text = "私の名前は";
 const url = "https://○○○○";  //取得するAPIのurl ※ユーザーデータを想定
 
 const json = await fetch(url)
  .then(res=> {
    console.log("非同期処理成功")
    return res.json(); 
  }).catch(error => {	 
    console.log("非同期処理失敗", error);
    return null;
  });
   
  const name = json.name; 
  console.log(`${text}${name}です。`);
 }
 
 GetUserName();
//======== consoleの結果
//非同期処理成功
//私の名前はjohnです。

asyncとawaitはセットで使います。
今回だとfetchの前にawaitを書くことで、fetchの処理が終わらないと次の処理に進まないようになります。
asyncはawaitを使うためにその関数につけるイメージです。
詳しい解説はできませんが自分はそのような覚え方をしちゃっています。。

間違っている点があればまたお知らせください。

Discussion

ログインするとコメントできます