理系公務員のプログラミング日記

WebAPIとJavaScriptの非同期処理について

タグ:
JavaScript

Web API について

API(Application Programable Interface) とは、プログラムを使って、外部のOSやソフトウェアを利用できるようにした仕組みのことを指します。

例えば、「Windows API」というものを利用することで、プログラムから簡単にWindowsのファイルシステムを操作することができます。

このうち、Web APIというものは、このAPIのやりとりをHTTP/HTTPSベースで実現することを指します。

HTTPのGetメソッドPostメソッドなどを決められた規則に従って利用することで、相手のAPI サーバーから、データを取得したり登録することができます。

JavaScriptでは、このようなWeb APIを利用するときには、 非同期処理というものが利用されます。

歴史的な経緯から非同期処理を実現する方法がいくつか存在しますが、最新のasync/awiat(エィシンク/アウェイト) を使用してみます。

とりあえず fetch と async と await を使ってみる。

Web APIの分かりやすい例として、気象庁のWeb APIから今日の天気予報のデータを取得してみます。

(実際は公式のAPIサービスというわけではなく、リクエストしたら JSON データが取得できる状態だそうです。)

参考:気象庁の天気予報JSONファイルをWebAPI的に利用したサンプルアプリ

JavaScriptで外部からデータを取得するには、非同期処理と一緒に、fetch()メソッドというものを利用します。

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body></body> <script> // API配信のURL // 130000は東京のエリアコード const url_Tokyo = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json" const getWeather = async (url) => { // 指定したurlのAPIからjsonデータを取得 const response = await fetch(url) // jsonデータをオブジェクトに変換する return response.json() } const getData = async () => { const data = await getWeather(url_Tokyo); console.log(data); console.log(data.publishingOffice); console.log(data.reportDatetime); console.log(data.targetArea); console.log(data.text); } getData() </script> </html>

下記のような天気予報の概要が取得できます。現在は梅雨です。

天気予報サンプル

非同期処理について

JavaScrip は シングルスレッド でのみ実行される言語です。
スレッドとは1つの処理の流れのブロックみたいなものです。
Java では マルチスレッド といって、複数のコードを並列して実行する機能がサポートされています。
マルチスレッドの機能が利用できると、メインの処理を1つのスレッドで動かす裏で、サーバーから画像や動画などのデータをダウンロードするという時間のかかる処理を別のスレッドで行うことができます。
しかし JavaScript はマルチスレッドが利用できません。そのため、同時に1つの処理しかできないため データをダウンロードする ような時間のかかる処理が始まると、別の処理がストップしてしまいます。

この問題に対処する仕組みが 非同期処理 です。

ちなみに addEventListener("click",function(){}) で定義されるイベント処理は非同期処理の一種になります。

Promise(ES2015~)

JavaScript の非同期処理は、その実行結果が Promise オブジェクト に含まれているという形で表現します。
ここで、データをダウンロードする非同期処理を考えます。データのダウンロードという処理は、問題なく成功する場合と通信エラーなどで失敗する場合があります。

Promise オブジェクトはこの成功したときの処理を resolveメソッド 、失敗したときの処理を rejectメソッド で定義し、こんな感じで Promise インスタンスを作成します。

const promise = new Promise((resolve, reject) => { resolve( // 非同期処理として実行したい内容 ); reject( // 失敗したときに実行したい内容(エラーを表示など) ); }); // 簡略化した書き方 const promiseResolve = Promise.resolve() const promiseReject = Promise.reject()

サンプルで利用したfetch()メソッドは、データのダウンロードという処理をこの Promise オブジェクトを利用して表現しています。
fetch() メソッドは戻り値として指定した url からダウンロードするという処理をresolve()の中に持った Promise オブジェクトを返してくれます。

Promise が実装されて以降、データのダウンロードなどの処理を行うライブラリはほとんどが戻り値は Promise オブジェクトの形になっています。
ですので、最初の頃は new Promise() といった処理を書く機会は少ないと思います。

async/await(ES2017~)

async/await は基本的にはセットで使います。

async は関数定義を行うときに使用するキーワードです。async をつけて定義した関数は戻り値として Promise インスタンスを返すようになります。

async function doAsync() { return "値"; } // アロー関数で書く const doAsync = async () => { return "値"; } // 同じ意味 function doAsync() { return Promise.resolve("値"); } //これも同じ function doAsync() { return new Promise((resolve, reject) => { resolve("値"); }); }

awaitasync で定義された関数内で利用できる式です。
右辺に Promise インスタンスを置きます。そのPromise インスタンスが成功または失敗するまで待機し、完了し次第式の処理を再開します。

async function doAsync() => { // 非同期処理 } async function asyncMain() => { // doAsync完了まで待つ // doAsyncが成功すれば、その結果をnumに代入 // doAsyncが失敗すれば、エラーを発生させる const num = await doAsync(); // 次の行はdoAsyncの非同期処理が完了されるまで実行されない console.log("この行は非同期処理の完了後に実行される"); }

非同期処理は少し難解な箇所ですが、サーバからデータを取得するという頻繁に行う動作で必須ですのでぜひ理解してください。

参考

気象庁の天気予報 JSON ファイルを WebAPI 的に利用したサンプルアプリ
新しい気象庁サイトから JSON データが取得できる件
JavaScript Promise の本