JavaScript 再入門(その24) 非同期通信 1 fetch

非同期通信 fetch

fetch オブジェクトは、XMLHttpRequest(XHR) オブジェクトに代わる、サーバーと対話するためのAPIです。
fetch はプロミス値を返しますので、XMLHttpRequest に比べて、簡略化されて可読性の良いスクリプトを書くことができます。

この記事では、演習用のJSONデータとして MDN が用意しているデータを利用します。

https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json

fetch を使ってサーバーからデータを取り込む方法を説明するのが目的なので、データの中身はあまり気にしないでください。

fetch() メソッド

fetch() メソッドの基本構文

let promise = fetch( URL , {オプション} ) ;

非同期関数を使って、サーバーからデータを取得

では、サーバーからデータを取得してみましょう。
非同期関数 ( async / await ) を使って、データを取得してみます。

async function getData() {
	const response = await fetch('https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json') ;
	const resData = await response.json() ;
	console.log( 'response' , response ) ;
	console.log( 'resData' , resData ) ;
	console.log( resData.squadName ) ;  // Super Hero Squad
}
getData() ;
/* 
response Response {type: 'cors', url: 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json', redirected: false, status: 200, ok: true, …}
resData {squadName: 'Super Hero Squad', homeTown: 'Metro City', formed: 2016, secretBase: 'Super tower', active: true, …}
*/

fetch() メソッドは Response オブジェクトを返します
fetch() メソッドによりサーバーへリクエストが送られると、すぐにプロミスが “pending” で返されます。その後サーバーがヘッダを受け取り通信状態を応答すると、Response オブジェクトで resolve します。Response オブジェクトには通信状態を示す、status (HTTPステータスコード) / ok (HTTPステータスコードが200~299の時 true)などが返されます。

但し、この時点では、サーバーとの通信(リクエスト)が確立されただけで、実際のデータは取得されていません。

Response オブジェクトの json() メソッドにより、JSON として解釈した結果で resolve するプロミスを返します。Responseオブジェクトのストリームを取得して、データを読み取り後JSONとして解釈し、JavaScriptのオブジェクトで resolve します。

エラー処理を入れてみます。

async function postData() {
	try {
		const response = await fetch('https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json') ;
		if ( !response.ok ) {
			throw 'FETCH ERROR! HTTP Status:' + response.status ;
		}
		const resData = await response.json() ;
		console.log( response ) ;
		console.log( resData ) ;
	} catch( e ) {			// ***
		console.error(e) ;
	}
}
postData() ;

スクリプトでは Response オブジェクトの ok プロパティでエラー処理の判断をしていますが、status プロパティを使えばもっと詳しくエラー処理をおこなうことも可能です。

プロミスチェーンで書く

非同期関数を使わないスクリプトです。

fetch('https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.jsonX')
	.then( ( response ) => {
		console.log( response ) ;
		return response.json() ;
	}).then( ( resData ) => {
		console.log( resData ) ;
		console.log( resData.squadName ) ;  // Super Hero Squad
	}).catch( ( e ) => {
		console.error( e ) ;
	}) ;

JSONデータ取得関数にしてみる。

少しだけ汎用性をもたせて、URLとパラメータを渡すとJSON形式でデータが取得できる関数を作成します。
今回は渡したパラメータが、ホスト側で利用されることはありませんが、スクリプトの例としてパラメータを渡しています。

リクエスト先の URL が変わっていますが、POSTする場合はCROS(Cross-Origin Resource Sharing)でエラーとなりますので、ローカル環境にJSONを作成しています。

// 汎用JSON取得関数
async function postData( argURL , argData ) {
	try {
		const response = await fetch(
			argURL , {
				method: 'POST' ,									// メソッドPOST
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify( argData ),					// JSONでパラメータを渡します。型は "Content-Type" ヘッダーと一致させる必要があります
			}
		) ;
		if ( !response.ok ) {										// fetchのエラーチェック
			throw 'FETCH ERROR! HTTP Status:' + response.status ;	// fetchでエラーがあればエラーを投げる
		}
		const resData = await response.json() ;						// レスポンスデータのの取得
		return {													// データの取得状態(true)と取得したJSONデータを返します。
			status: true ,
			retData: resData ,
		} ;
	} catch( e ) {													// エラー処理
		return {													// データの取得状態(false)とエラー内容を返します。
			status: false ,
			retData: e ,
		} ;
	}
}
// プロフィール表示関数
async function getProfile( argName ) {
	data = { name : argName } ;
	try {
		const res = await postData( 'http://localhost/testsrc/files/superheroes.json' , data) ;
		if ( !res.status ) {										// データの取得状態のチェック
			throw res.retData ;										// 正常に取得できなかったらエラーを投げてcatch()で処理する。
		}
																	// データが正常に取得できた処理
		const retData = res.retData ;
		console.log( argName + ' ' + retData.squadName ) ;			// Heroes Super Hero Squad
	} catch( e ) {													// エラーの処理
		console.error(e) ;
	}
}
getProfile( 'Heroes' ) ;

汎用JSON取得非同期関数 postData は、データ取得URL とパラメータ用データを渡すと、取得状態とデータを受け取ることができます。プロフィール表示関数 getProfileは、この汎用JSON取得非同期関数 postData を使って取得したデータを表示しています。

JSON以外のオブジェクトの取得

今回の例では、JSON データを取得してますが、Response オブジェクトには、それ以外にもメソッドが用意されています。

  • Response.arrayBuffer()
    レスポンスの本文を表す ArrayBuffer で解決するプロミスを返します。
  • Response.blob()
    レスポンスの本文を表す Blob で解決するプロミスを返します。
  • Response.clone()
    Response オブジェクトの複製を生成します。
  • Response.error()
    ネットワークエラーに関連した新しい Response オブジェクトを返します。
  • Response.formData()
    レスポンスの本文を表す FormData で解決するプロミスを返します。
  • Response.json()
    レスポンスの本文のテキストを JSON として解釈した結果で解決するプロミスを返します。
  • Response.redirect()
    異なる URL で新しい response を生成します。
  • Response.text()
    レスポンスの本文のテキスト表現で解決するプロミスを返します。

参考リンク

MDN 開発者向けのウェブ技術 > WindowOrWorkerGlobalScope.fetch()
MDN 開発者向けのウェブ技術 > Fetch API
MDN 開発者向けのウェブ技術 > Response
MDN 開発者向けのウェブ技術 > Response.json()
MDN 開発者向けのウェブ技術 > Fetch の使用
ウェブ開発を学ぶ > JavaScript > クライアントサイドWebAPI > サーバからのデータ取得
MDN 開発者向けのウェブ技術 > HTTP > オリジン間リソース共有 (CORS)
MDN 開発者向けのウェブ技術 > JavaScript「再」入門