JavaScriptを勉強していたら、async/await
って言葉が出てきたんだけど、よくわからない。
本記事では、上記の悩みを解決します。
僕も業務ではあまり使った経験は少ないのですが、データを取得する処理などを作る際には必要な知識になります。
特にWeb制作だと使用する場面はほとんどないかと思います。
ですが、Web開発をする際には必ず必要な知識なのでぜひ覚えていきましょう^^
本記事の信頼性
30歳から異業種への転職をして、Shopify Experts企業で1年半ほどフルリモートで勤務していました。
現在は名古屋の自社開発企業のフロントエンドエンジニアしています。フリーランスとしても活動しています。
今回は、ローカルでプロジェクトを作成して実際にQiitaの記事を取得して表示させることを行います^^
async/awaitとは?
async
とawait
を使った非同期処理の構文のことを言います。
非同期処理を行う方法はasync/await
以外にもあり、コールバック関数やPromiseといったものがあります。
僕の理解だと、async/await
が現在非同期処理を行うもっと簡単な方法です。
どのようにasync/await
が生まれたのかを調べて記事にしてみようと思います。
公式ドキュメント→(https://jsprimer.net/basic/async/)
async/awaitはどんな場面で使うことができるの?
async/await
は、非同期処理を行う様々な場面で使うことができますが、特にWeb APIやデータベースへのクエリなどにおいてその便利さが顕著に表れます。
Google Mapの機能は、非同期処理の活用例として特に有名です。例えば、ある地域の地図を見ていて隣町の地図を見たくなったとき、ドラッグアンドドロップで地図を移動すると、その裏で非同期処理により隣町のデータが取得されます。これにより、ユーザーは遅延を感じることなくスムーズにマップを見ることができます。
非同期処理がなければ、画面を移動するたびに「データを取得する」ボタンを押して新しいデータを取得しなければならないなど、ユーザー体験が大きく損なわれます。
このように、非同期処理はユーザー体験の向上に大きく寄与することができます。そして、その非同期処理を簡潔に、かつ直感的にコードに落とし込むのが、async/await
という強力な機能です。
実際に使ってみよう!
それでは、 実際のasync/await
を使ってみようと思います!
今回は、QiitaをWeb APIを使用して、自分が投稿した記事の一覧を取得してみようと思います!
Qiitaでトークンを発行する
まずは、Qiitaの管理画面から設定をクリックします。
アプリケーションを選択して、「新しくトークンを発行する」を選択します。
アクセストークンの説明には任意のテキストを入れて、スコープはデフォルトのそのままの設定で大丈夫です。
発行すると、トークンが表示されます。この文字列はページを移動してしまうともう確認できなくなってしまうので必ずメモをしておきましょう!
これで、Qiitaの記事を取得する準備ができました。
Macのターミナルで記事を取得してみる
実際に、JavaScriptのasync/await
を使って記事を取得してみる前に、まずは本当に記事を取得できるのかをターミナルで確認してみます。
今回記事を取得するためのエンドポイント→ https://qiita.com/api/v2/authenticated_user/items
Qiitaのエンドポイントはこちらで確認することが可能です。
公式ドキュメント→(https://qiita.com/api/v2/docs)
ターミナルに下記を入力してみます。
$ curl "https://qiita.com/api/v2/authenticated_user/items"
すると下記の結果が返ってきます。権限がないようです。
{"message":"Unauthorized","type":"unauthorized"}%
次にトークンを含めてリクエストを送ってみます。その際、Authorization
ヘッダにBearer {あなたのトークン}
を指定します。
curl -H "Authorization: Bearer e2d7081afdc38fa740decdbe0869def5062d6d3a" "https://qiita.com/api/v2/authenticated_user/items"
すると、下記のように記事をJSON形式で取得できているのが分かります。
それぞれの記事には、rendered_body
(HTMLでレンダリングされた記事本文)、body
(マークダウン形式の記事本文)、created_at
(作成日時)、likes_count
(「いいね」の数)、title
(記事のタイトル)、url
(記事のURL)などの情報が含まれています。
これで問題なく、記事を取得できることが確認できたので次に実際にプロジェクトを作って、JavaScriptのasynce/await
を使って記事一覧を取得してブラウザで表示してみよう思います。
プロジェクトを作成しよう
下記のような構成でプロジェクトを作成しましょう。
my-app
├ css
│ └ style.css
├ js
│ └ script.js
└ index.html
今回のテーマとはそれるのでindex.html
とstyle.css
のコードの中身の細かい説明はしませんが参考までに。
ulタグのidのqiita-posts
をscript.js
で取得して、その中にAPIで取得した記事を表示させていきます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href=".//css/style.css" />
<title>Qiitaの記事一覧を取得する</title>
</head>
<body>
<div class="inner">
<h2>Qiitaの記事一覧</h2>
<ul id="qiita-posts" class="p-postList"></ul>
<script src="./js/script.js"></script>
</div>
</body>
</html>
/* style.css */
body {
font-family: Arial, sans-serif;
margin: 0;
}
.inner {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 0 15px;
}
.p-postList {
list-style-type: none;
width: 100%;
padding: 0;
display: grid;
grid-gap: 20px;
margin: 0 auto;
@media (min-width: 768px) {
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
}
}
.p-postList li {
border: 1px solid #ddd;
margin-top: -1px;
background-color: #f6f6f6;
padding: 12px;
font-size: 18px;
}
.p-postList li:hover {
background-color: #ddd;
}
ちなみに、style.cssの中身はChatGPTに適当に作ってもらったぜ。
async/awaitの処理を書こう
最終的なコードがこちらになります。
async function fetchMyQiitaPosts() {
const TOKEN = "4a450fd5689413af28d740bd08e0da7f1de7d8fe";
const url = "https://qiita.com/api/v2/authenticated_user/items";
const options = {
method: "GET",
headers: {
Authorization: `Bearer ${TOKEN}`,
},
};
let response = await fetch(url, options);
if (response.ok) {
let json = await response.json();
let postsList = document.getElementById("qiita-posts");
for (let post of json) {
let listItem = document.createElement("li");
// Format the date strings
let createdAtDate = new Date(post.created_at);
let updatedAtDate = new Date(post.updated_at);
let formattedCreatedAt = createdAtDate.toLocaleDateString();
let formattedUpdatedAt = updatedAtDate.toLocaleDateString();
let textNode = document.createTextNode(
`${post.title} (作成日: ${formattedCreatedAt}, 更新日: ${formattedUpdatedAt})`
);
listItem.appendChild(textNode);
postsList.appendChild(listItem);
}
} else {
console.log("HTTP-Error: " + response.status);
}
}
fetchMyQiitaPosts();
分解してコードを確認していきます。
非同期関数として、 fetchMyQiitaPosts()
を定義しています。
async function fetchMyQiitaPosts()
以下では、変数として必要な情報を定義しています。
TOKENには、先程取得した値を入れてください。
urlの部分には、Qiitaの記事を取得する際の、エンドポイントが入ります。(公式ドキュメント)
optionsには、HTTPリクエストのオプションを設定するオブジェクトです。ここではGETメソッドを使用し、ヘッダーに先ほどのアクセストークンをBearerトークンとして設定しています。
const TOKEN = "4a450fd5689413af28d740bd08e0da7f1de7d8fe";
const url = "https://qiita.com/api/v2/authenticated_user/items";
const options = {
method: "GET",
headers: {
Authorization: `Bearer ${TOKEN}`,
},
};
次に、上記で設定したURLとオプションを使用して、非同期のHTTPリクエストを行い、その結果をresponse
変数に格納しています。fetch
関数はPromiseを返すので、await
を用いて非同期処理を行います。
ドキュメント→(https://developer.mozilla.org/ja/docs/Web/API/fetch)
let response = await fetch(url, options);
次に条件分岐の部分になります。Responsの中に、okというプロパティがあるのでそちらで条件分岐を行っています。
ドキュメント→(https://developer.mozilla.org/ja/docs/Web/API/Response/ok)
if (response.ok) {
// 成功した処理
} {
console.log("HTTP-Error: " + response.status);
}
成功したとき
失敗したとき
次に、成功したときの処理の中身です。
let json = await response.json();
let postsList = document.getElementById("qiita-posts");
response.json()
メソッドはPromiseを返す非同期操作です。この関数はHTTPレスポンスの本文を読み取り、それをJSON形式として解析します。
この解析処理は時間がかかる可能性があるため、非同期で実行されます。つまり、この処理が完了するまでの間にも、JavaScriptの他のコードの実行が可能です。
await
キーワードを使用すると、Promiseが解決される(つまり非同期操作が完了する)のを待つことができます。await
を使ってresponse.json()
を呼び出すと、この関数が完了し、結果のJSONデータが返されるまで次の行に進むことはありません。これにより、非同期操作を行うコードを、同期的に実行するコードのように直感的に書くことができます。
したがって、let json = await response.json();
の行では、HTTPレスポンスの本文をJSONとして解析し、その結果を json
変数に格納するために await
を使用しています。
HTTPのレスポンスの中身はこちらになります。
postList
はセレクターを取得しています。特に説明不要ですねこちらについては^^
次にループ処理の部分です。ここで実際に画面に表示するHTMLを生成しています。
for (let post of json) {
let listItem = document.createElement("li");
let createdAtDate = new Date(post.created_at);
let updatedAtDate = new Date(post.updated_at);
let formattedCreatedAt = createdAtDate.toLocaleDateString();
let formattedUpdatedAt = updatedAtDate.toLocaleDateString();
let textNode = document.createTextNode(
`${post.title} (作成日: ${formattedCreatedAt}, 更新日: ${formattedUpdatedAt})`
);
listItem.appendChild(textNode);
postsList.appendChild(listItem);
}
先程json
変数に格納した情報をループ処理で各投稿をpost
の変数として扱います。
下記がpost
の中身です。console.log("post: ", post)
で確認することができるのぜひご自身でもどのような値が入っているのかは確認してみてください^^
あとの処理はHTMLを生成するものなので、今回は省略します。
これでブラウザで確認すると、正しく記事の取得ができているのが確認できます。
まとめ
結構なれない処理や単語が多くて初見だと難しく感じるかと思いますが、まずはコピペで動かしてみてわからない単語などを調べたり変数の中身を確認しながら理解していくのがいいかなと思います。(僕も毎回console.log
で確認します笑)