LoginSignup
10
6

More than 1 year has passed since last update.

[Javascript]コールバックヘルからの帰還。Promise/Async/Awaitを考えるよ。

Last updated at Posted at 2021-07-02

Promise, Async, Await 

以前に書いたように、Promiseはコールバック地獄に落ちたプログラマーを救うために現れた救世主である。
で、その正体はただのオブジェクトだ。
これが結構重要。なので、Promiseが返ってくる というよく分からない表現は、単にPromiseというオブジェクトが返ってくると考えれば良い。(Promiseが何かよく分からなかった時、調べる度にPromiseとは値ではなくPromiseを返すのがPromiseなのだ!って説明されていて、そもそもPromiseが分からないのに、その分からないものを返すものとかってだから何やねん!ってPCの前で憤慨した思い出)
で、その後に便利なPromiseを色々な関数に適応できるようにして、使い勝手を良くしたのがAsync/Awaitである。
これを使うと、普通の関数をPromise関数に変換できるのである。
簡単な例で説明しよう。

const hello = (name) =>{
  return `Hello ${name}`
}

hello("namahage");


//戻り値 : Hello namahage

これはいわゆる普通の関数。ただただ何かの値が返ってくるだけ。
で、この関数をプロミス関数化したものがこれ。

const hello = async (name) =>{
  return `Hello ${name}`
}

hello("namahage");

関数の前にasyncを付けるだけ!
で、何か違うんか?
こうすると、関数実行後にPromiseが返ってくるんじゃ!
Promiseが返ってくるとはなんぞや!?
実行結果がこれ。

//戻り値
Promise {<fulfilled>: "Hello namahage"}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "Hello namahage"

fulfilledっていう実行結果と、"Hello namahage"というバリューがオブジェクトとして返ってきておるじゃろ。
さっきの、バリューだけがちょろっと返ってきてるのとは大きな違いじゃ。
で、このfulfilledっていう合図が、コールバック地獄を抜け出す大きな手段になるのじゃ。
コールバック、非同期処理をする意味っていうのは、ある順番に沿って関数を実行させていく為じゃの?野菜を洗ったら→刻む→炒める→盛り付ける。 ばらばらじゃと、炒める→盛り付ける→切る→洗う とかになって、大惨事じゃぁ!
例えば、サーバーに何らかのデータを要求する、で、それが戻ってきてからブラウザに表示。っていう場合、ブラウザ表示はデータがちゃんと戻ったよ という合図があってから実行しないと、何もない画面が表示されるだけじゃ。おまけに、こういうサーバーを通す処理は時間がかかる。
で、この合図がfulfilled。
そして、Promiseオブジェクト化するっていう事は、thenメソッドを使えて、それを繋げることで今までコールバックでやっていた順番に関数を実行 っていうのが出来るのじゃ。

const hello = async (name) =>{
  return `Hello ${name}`
}

hello("namahage").then((result)=>{
  console.log(result);
})

// Hello namahage

分かるかの?hello()関数が実行されたあとに、戻り値が自動的に.then関数の引数として渡されるんじゃ。これはまさにコールバック的な動きじゃな。で、このthenもPromiseを返すので、同様に.thenでつないでいけば、多くの関数も連続で順番通りに実行していけるんじゃな。
で、Promiseは成功時にFulfilled、何かエラーがあった時はrejectを返すと説明したな。
rejectの時の対応は、thenチェーンの最後に .catch でエラーのハンドリングをするのがお決まりのパターンじゃ。

const hello = async (name) =>{
  return `Hello ${name}`
}

hello("namahage")
.then((result)=>{
  console.log(result);
})
.catch((error)=>{
  console.log(error)})
}

Async/Await

で、次は await じゃ。
このasync/awaitは前に述べたように、Promise登場数年後にjavascriptに実装されたのじゃが、Promiseをベースに、さらに使い勝手をよくしようという目論見じゃ。
Promiseのthenチェーンよりもよりシンプルで可読性の良いコードを書けるんじゃ。勿論.thenチェーンとの併用も可能。
awaitはasyncでPromise化した関数の中でセットで使うことが出来て、その言葉のままじゃな。.thenと似ておって、fulfilledなりrejectの合図が戻ってくるまで、awaitを付けたコードブロックの実行を待ってくれるのじゃ。
また、node.jsとは何かで述べたように、この待ち時間に色々と他の処理を非同期でやってくれるので、CPU負荷も低く、全体のスピードもかなり速くなるのじゃ。
node.jsはこういう処理をサーバー側でやっておるんじゃの。

基本のシンタックス。

let value = await promise;

promise関数から合図が返ってくるのを待ち、返ってきた返り値を変数に入れている。
以下のサンプルコードを見てみよう。

async function showAvatar() {

  // read our JSON
  let response = await fetch('/article/promise-chaining/user.json');
  let user = await response.json();

  // read github user
  let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
  let githubUser = await githubResponse.json();

  // show the avatar
  let img = document.createElement('img');
  img.src = githubUser.avatar_url;
  img.className = "promise-avatar-example";
  document.body.append(img);

  // wait 3 seconds
  await new Promise((resolve, reject) => setTimeout(resolve, 3000));

  img.remove();

}

showAvatar();

まず、javascriptビルドインのfetch()もjson()もすでにPromise関数である。 つまり、実行後にPromiseがかえってくるのじゃ。 
で、上でやっている事は、ユーザーの情報をとってきて、そのユーザー情報をもとに、githubのプロフィール写真をfetch。
.json()でフェッチしたJSONをjavascriptオブジェクトに変換。
全ての素材がそろったら、画像を表示して、3秒後に消す。っていう事をやっている。
fetch()は素材をとってくるのに時間がかかるから、Promiseでの非同期処理が必須になってくる。Promiseなかったら、データが届いて無いから、写真が表示されなかったりエラーになったりするからの。
では、そろそろバイトの時間なので、これくらいにしとくぞ。

コード参照:
https://javascript.info/async-await

10
6
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6