#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なかったら、データが届いて無いから、写真が表示されなかったりエラーになったりするからの。
では、そろそろバイトの時間なので、これくらいにしとくぞ。