以前、Fetch APIでJSONデータを読み込みました。この時はthen()
で処理を繋ぐ書き方をしていました。async/await構文で書くこともできて、そちらの方がよく使われているとのことで、処理を書き換えてみました。それではいきましょう!
【目次】
- async/await構文
- then()を使ったFetch API
- Promiseを読み込む
- PromiseとFetch()とasync/await
- async/await構文のレスポンス
- async/awaitで取得したJSONデータを処理する
- DOM処理などの関数の分離
- 最後に
※参考:前回記事
【JSON】外部JSONファイル化したはっぴいえんどアルバム情報をFetch APIでDOMに読み込む - クモのようにコツコツと
※参考:ネイティブJSでいろいろやってみたシリーズまとめ
qiita.com
async/await構文
API通信はthen()よりasync/await構文
しまぶーさんの「APIを叩く」の解説動画を見ていたら、最近はthen()
よりも「async/await構文」の方がよく使われている模様。
どんなものなのか、早速体験したく!
then()の書き方
then()
メソッドの書き方。自分にとっては馴染み深い方法w
fetch(url).then(function(res) { return res.json(); }).then(function(json) { //処理 json.xxxx〜 });
fetch()
メソッドを実行。引数はAPIのURLthen()
メソッドで次の処理をつなぐ。引数は無名関数でその引数はレスポンスのres
url
のレスポンスres
のjson()
メソッドを実行し、結果(jsonデータ)をreturn
で返すthen()
で次の処理を繋ぐ。引数は無名関数でその引数はjson
json
を使った処理を書く
「then」は「その後」という意味で、then()
で繋ぐと「非同期通信が終わったら次の処理を行う」という挙動になる。
ajaxは非同期通信(並行処理)のため処理順を設計するのがポイントになる。非同期通信が終わる前に次の処理をすると「データが見つからない」エラーになる。
async/await構文の書き方
先程のthen()
の書き方をasync/await構文に置き換えるとこうなる。
//json読み込み async function hoge () { const res = await fetch(url); const json = await res.json(); // 処理 json.xxxx〜 }
- 関数の
function
宣言の前にasync
を書いて非同期(async)関数であることを宣言 - 変数
res
にawait
を書くことで非同期通信が終わった後にfetch()
メソッド(引数はurl
)を実行。 - 変数
json
にawait
を書くことで非同期通信が終わった後にres.json()
を実行 res
、json
が終わったら次の処理を実行する
async
は「非同期」という意味で、Ajax(Asynchronous JavaScript + XML)のAと同じ。
await
は「待つ」という意味で、先程のthen()
と同じような役割かな。
then()を使ったFetch API
前回作ったものこちら。thren()
を使ったFeatch APIでJSONデータを読み込んでいる。
See the Pen fetch api 03 by イイダリョウ (@i_ryo) on CodePen.
はっぴいえんどのアルバム情報を表示している。
JSコード全体
const url = https://hoge/fuga.json // jsonファイルのURL //json読み込み fetch(url) .then(function(response) { return response.json(); }) .then(function(json) { //外枠アクセス var spec = document.querySelector('.spec'); //テンプレ複製 //(アルバム枚数-1枚) for (var i=1; i < json.length; i++) { var clone = spec.firstElementChild.cloneNode(true); spec.appendChild(clone); } //タイトル、発売日、曲名アクセス var specTitle = document.querySelectorAll('.title'); var specRelease = document.querySelectorAll('.release'); var specTrack = document.querySelectorAll('.track'); //アルバムデータ入力(アルバム枚数分) for (var i=0; i < json.length; i++) { //タイトル入力 specTitle[i].innerHTML = json[i].title; //発売日入力 specRelease[i].innerHTML = json[i].release; //曲名入力(曲数分ループ) for(var t=0; t < json[i].track.length; t++) { var list = document.createElement('li'); list.innerHTML= json[i].track[t]; specTrack[i].appendChild(list); } } });
※参考:前回記事
【JSON】外部JSONファイル化したはっぴいえんどアルバム情報をFetch APIでDOMに読み込む - クモのようにコツコツと
これをasync/await構文に書き換えてみんと欲す!
Promiseを読み込む
まずはPromiseの読み込みを体験してみる。はて?Promiseとはなんぞや?
作ったもの
作ったものこちら!
See the Pen fetch api -Async/Await 01 by イイダリョウ (@i_ryo) on CodePen.
っておい!画面真っ白じゃないか!(食い気味に)いいんです!!今回の処理はコンソールへの表示です。
JSコード
const url = https://hoge/fuga.json // jsonファイルのURL //json読み込み const res = fetch(url); console.log(res); /* .then(function(response) { return response.json(); // 中略 }); */
- 変数
res
でfetch()
メソッド実行 - コンソールに
res
(結果)を表示
やってることはこれだけ。他の処理はコメントアウトした。
コンソール確認
そうするとコンソールにこんなレスポンスが! はい来た「Promise」!
オブジェクトになってるので開いてみるとめちゃめちゃ深い階層で情報量満載w
fetch()
メソッドを実行するとこのPromiseオブジェクトが返される。この中から処理に必要な情報だけを取り出す必要がある。このままだと使いにくい…
PromiseとFetch()とasync/await
Promiseとは
これまでもPromiseを読み込んでたわけだ。Promiseとはなんぞや。意味は「約束する」。
Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。
Promise インターフェイスは作成時点では分からなくてもよい値へのプロキシです。Promise を用いることで、非同期アクションが最終的に成功した時の値や失敗した時の理由に対するハンドラーを関連付けることができます。これにより、非同期メソッドは、最終的な値をすぐに返す代わりに、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになります。
※参考:Promise - JavaScript | MDN
Promiseとは非同期通信の結果(失敗した理由も含む)全ての情報なんだな。
Fetch()メソッドでPromiseが返される
このPromiseを返すのがFetch()
Fetch API には (ネットワーク越しの通信を含む) リソース取得のためのインターフェイスが定義されています。
fetch() メソッドは必須の引数を1つ取り、取得したいリソースのパスを指定します。成功か失敗かに関わらず、リクエストに対する Response に解決できる Promise を返します。
async/await構文
で、async/awaitです。まずはasync function宣言から。
async function 宣言は、 非同期関数 — AsyncFunction オブジェクトである関数を定義します。非同期関数はイベントループを介して他のコードとは別に実行され、結果として暗黙の Promise を返します。
※参考:async function - JavaScript | MDN
次にawait演算子
await 演算子は、async function によって Promise が返されるのを待機するために使用します。
ふむふむ。ではthen()
メソッドは?
then() メソッドは Promise を返します。最大2つの引数、 Promise が成功した場合と失敗した場合のコールバック関数を取ります。
※参考:Promise.prototype.then() - JavaScript | MDN
やはりawait
とthen()
は似たような位置づけか。
async/await構文のレスポンス
作ったもの
先程のPromiseからasync/await構文で必要な情報を取り出す
See the Pen fetch api -Async/Await 02 by イイダリョウ (@i_ryo) on CodePen.
また画面上は白いまま。コンソールに表示されているレスポンスが変わっている。
JSコード
先程のfetch()
メソッドの部分を変更した。
//json読み込み async function resJson () { const res = await fetch(url); const albums = await res.json(); console.log(albums); } resJson();
async function
宣言で関数resJson()
を作成- 変数
res
でawait
演算子付きfetch()
メソッドを実行。引数はurl
- 変数
albums
でawait
演算子付きres.json()
メソッドを実行 - コンソールに
albums
(JSONのレスポンス)を表示 resJson()
を実行
関数を定義するだけではAPIを読みにいかないので関数を実行もする。(この関数をクリックなどのイベントに入れると実行タイミングを制御できる)
コンソール確認
コンソールはこうなった
JSONデータの中の配列3つが返って来とる!
はっぴいえんどの3枚のアルバム名がある!
1つ目を開くとファーストアルバムの詳細情報!
曲名も全て読み込まれてる!
async/awaitで取得したJSONデータを処理する
async/awaitで無事にJSONデータは取得できた。次はこのデータを使ってDOM処理をする。
作ったもの
作ったものこちら!
See the Pen fetch api -Async/Await 03 by イイダリョウ (@i_ryo) on CodePen.
今度ははっぴいえんどのアルバム情報が画面にも表示されてる!
JSコード
const url = https://hoge/fuga.json // jsonファイルのURL //json読み込み async function resJson () { const res = await fetch(url); const albums = await res.json(); console.log(albums); // DOM const spec = document.querySelector('.spec'); //テンプレ複製 //(アルバム枚数-1枚) for (let i=1; i < albums.length; i++) { let clone = spec.firstElementChild.cloneNode(true); spec.appendChild(clone); } // Dom const specTitle = document.querySelectorAll('.title'); const specRelease = document.querySelectorAll('.release'); const specTrack = document.querySelectorAll('.track'); //アルバムデータ入力(アルバム枚数分) for (var i=0; i < albums.length; i++) { //タイトル入力 specTitle[i].innerHTML = albums[i].title; //発売日入力 specRelease[i].innerHTML = albums[i].release; //曲名入力(曲数分ループ) for(var t=0; t < albums[i].track.length; t++) { var list = document.createElement('li'); list.innerHTML= albums[i].track[t]; specTrack[i].appendChild(list); } } } // 関数実行 resJson();
- 先程コメントアウトしたDOM処理などを
resJson()
関数に入れる - 前回の
then()
のときjson
に繋げていた処理をalbums
に変更する
DOM処理の詳細は前回の記事を参照(json
をalbums
に変更しただけで動いた!)
※参考:前回記事
【JSON】外部JSONファイル化したはっぴいえんどアルバム情報をFetch APIでDOMに読み込む - クモのようにコツコツと
DOM処理などの関数の分離
作ったもの
作ってみてresJson()
関数の中の処理が膨らみ過ぎているように感じたので、関数の分離を行った。
See the Pen fetch api -Async/Await 04 by イイダリョウ (@i_ryo) on CodePen.
画面表示上は変更なし
JSコード(全体)
const url = https://hoge/fuga.json // jsonファイルのURL //json読み込み async function resJson () { const res = await fetch(url); const albums = await res.json(); return albums; } // アルバム複製 function albumsClone(albums) { // DOM取得 const spec = document.querySelector('.spec'); //テンプレ複製 //(アルバム枚数-1枚) for (let i=1; i < albums.length; i++) { let clone = spec.firstElementChild.cloneNode(true); spec.appendChild(clone); } } // アルバム情報入力 function albumsInput(albums) { // Dom取得 const specTitle = document.querySelectorAll('.title'); const specRelease = document.querySelectorAll('.release'); const specTrack = document.querySelectorAll('.track'); //アルバムデータ入力(アルバム枚数分) for (var i=0; i < albums.length; i++) { //タイトル入力 specTitle[i].innerHTML = albums[i].title; //発売日入力 specRelease[i].innerHTML = albums[i].release; //曲名入力(曲数分ループ) for(var t=0; t < albums[i].track.length; t++) { var list = document.createElement('li'); list.innerHTML= albums[i].track[t]; specTrack[i].appendChild(list); } } } async function allAlbums() { // アルバム情報取得 const albums = await resJson(); // アルバム複製 albumsClone(albums); // アルバム情報入力 albumsInput(albums); } allAlbums();
resJson():JSON読み込み
最初にresJson()
関数でJSONデータを読み込む
//json読み込み async function resJson () { const res = await fetch(url); const albums = await res.json(); return albums; }
- async/await構文を使った
resJson()
関数を定義 - コンソールに表示してた時と同じ処理
- 最後に
return
でalbums
のレスポンス結果(JSONデータ)を返す
albums
を返すことで外側でもJSONデータの値を使うことができる。
albumsClone():アルバム複製
次はalbumsClone()
関数でアルバム複製部分。
// アルバム複製 function albumsClone(albums) { // DOM取得 const spec = document.querySelector('.spec'); //テンプレ複製 //(アルバム枚数-1枚) for (let i=1; i < albums.length; i++) { let clone = spec.firstElementChild.cloneNode(true); spec.appendChild(clone); } }
- アルバム複製部分を関数として分離
- 引数で
albums
のデータを読み込む
albumsInput():アルバム情報入力
albumsInput()
関数でアルバム情報入力部分の関数も作成
// アルバム情報入力 function albumsInput(albums) { // Dom取得 const specTitle = document.querySelectorAll('.title'); const specRelease = document.querySelectorAll('.release'); const specTrack = document.querySelectorAll('.track'); //アルバムデータ入力(アルバム枚数分) for (var i=0; i < albums.length; i++) { //タイトル入力 specTitle[i].innerHTML = albums[i].title; //発売日入力 specRelease[i].innerHTML = albums[i].release; //曲名入力(曲数分ループ) for(var t=0; t < albums[i].track.length; t++) { var list = document.createElement('li'); list.innerHTML= albums[i].track[t]; specTrack[i].appendChild(list); } } }
- アルバム情報入力部分を関数として分離
- 引数で
albums
のデータを読み込む
allAlbums():定義した関数を読み込む
最後にallAlbums()
関数の中でこれまで定義した関数を読み込む
async function allAlbums() { // アルバム情報取得 const albums = await resJson(); // アルバム複製 albumsClone(albums); // アルバム情報入力 albumsInput(albums); } allAlbums();
- async/await構文を使った
allAlbums()
関数を定義 - 変数
albums
でawait
付きでresJson()
を実行 albumsClone()
関数を実行。引数でalbums
を読み込むalbumsInput()
関数を実行。引数でalbums
を読み込む- 最後に定義した
allAlbums()
自体を実行
APIとの非同期通信をしているのはresJson()
なのでawait
をつける。
他の関数は引数でresJson()
で取得したalbums
のJSONデータを読み込む。
最後のallAlbums()
関数実行もクリックイベントとかの中に入れると実行タイミングを制御できる。
最後に
ということでasync/await構文でのAPI通信を体験できました!then()
の書き方に慣れていたんですが、同じことができることがわかったので、async
やawait
に遭遇したら非同期通信だな、と理解できそうです。
自分で書く際にもasync/await構文の方がthen
よりも処理を分離しやすいように感じたので、今後も積極的に使って行きたく思います。それではまた!
※参考:ネイティブJSでいろいろやってみたシリーズまとめ
qiita.com