fetch(url[, options]))
リクエストやレスポンスを操作し非同期のネットワーク通信を行い、その結果を含む Promise インスタンスを生成する。
引数 | url | 読み込むファイルの URL あるいは相対パス などを表す文字列 |
---|---|---|
options | オプション(JSON 形式のオブジェクト)下記参照 | |
戻り値 | リクエストに対するレスポンスを持った Promise インスタンス |
同期処理とは、プログラムの記述順に処理を実行する方式です。それに対して非同期処理は、プログラムの記述とは異なる順に実行する方式です。
同期処理は順番に実行してくれて分かりやすいですが、途中に時間がかかる処理があった場合、次の処理を行うことができません。例えば、サーバーと通信しながら画面を表示しているとき、サーバーから応答が返ってくるまで画面が止まってしまっては問題です。そのため、その対策として、非同期処理が行われます。
非同期処理は、次のオブジェクトで実現できます。
ただし、JavaScript はシングルスレッドで動いていますので、非同期とはいっても同時に2つの処理を実行することはできません。JavaScript では、同期であろうと非同期であろうと2つ以上の処理を並行して行うことはできません。しかし、他(例えば、データベースなど)に処理を任せている間に、その処理の終了を待たないで次の命令を実行できるという意味で非同期なのです。
fetch を使用するにあたって、fetch およびその他の必要なオブジェクトについて説明します。
注意
他のサーバー上のファイルはそのサーバーが許可していない限り読み込むことはできません。取得が許可されるには、サーバー側のレスポンスヘッダの Access-Control-Allow-Origin にアクセスする側の URI を追加してもらう必要があります。
非同期のネットワーク通信は、XMLHttpRequest によって提供されてきました。それに変わる次の Ajax 仕様として fetch が策定されています。
次の形式で fetch() を呼び出す場合、取得するリソースのパスを第1引数で指定します。呼び出し後、fetch() は Promise インスタンスを返します。fetch() によるリクエストが成功であっても不成功であっても、Promise は解決され、リクエストに対するレスポンスを取得することができます。
fetch() の第2引数は任意で、リクエストの初期化に使用されるオプションを指定します。
なお、Promise については、「12.2 非同期処理(Promise)」 を参照してください。
リクエストやレスポンスを操作し非同期のネットワーク通信を行い、その結果を含む Promise インスタンスを生成する。
引数 | url | 読み込むファイルの URL あるいは相対パス などを表す文字列 |
---|---|---|
options | オプション(JSON 形式のオブジェクト)下記参照 | |
戻り値 | リクエストに対するレスポンスを持った Promise インスタンス |
JSON 形式のオブジェクトは次のようなフォーマットを持ちます。
options のメンバーの、名前とその値には次のようなものがあります。
(注)オリジン:スキーム、ホスト、ポート番号の組み合わせ
<script> // promise インスタンスの生成 var promise = fetch("sample.php", // 実行ファイル { // オプション method: "post", headers: {"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"}, body: "name=Mary&gender=female" }); // 以下略 </script>
データの取得には、リクエストメソッドとして GET を指定します。
ファイルを取得する場合は、自サーバー上のファイルの URL か相対パスで指定します。他のサーバー上のファイルはそのサーバーが許可していない限り読み込むことはできません。
<span id="d1"></span><br> <!-- 以下略 --> <script> var d1 = document.getElementById("d1"); // 以下略 var promise = fetch("sample.txt", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => { // アロー関数で記述してるが、response => は function(response) と同じ意味 d1.textContent = response.url; d2.textContent = response.type; d3.textContent = response.status; d4.textContent = response.statusText; d5.textContent = response.ok; var headers = ""; response.headers.forEach((value, key) => headers += key + " : " + value + "<br>"); d6.innerHTML = headers; }) .catch(err => d1.textContent = err); </script>
ファイルを読み込んで、レスポンスの内容を表示しています。
headers() の返す値はオブジェクトなので、そのメンバーの名前と値をすべて表示しています。
ファイルの内容は、text() の返す値の中に Promise インスタンスとして格納されています。
<span id="d1" style="white-space:pre;"></span><br> <script> var d1 = document.getElementById("d1"); var promise = fetch("sample.txt", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => { // アロー関数で記述してるが、response => は function(response) と同じ意味 response.text().then(t => d1.textContent = t); }) .catch(err => d1.textContent = err); </script>
text() の返す値はファイルの内容を持った Promise インスタンスです。ファイルの内容は表示する span 要素に white-space:pre 属性を指定しているので、改行されてすべて表示されています。
ファイルの情報は、blob() の返す値の中に Promise インスタンスとして格納されています。しかし、ファイルの内容は、fetch しただけではまだ読み込まれていませんので、この例では FileReader で読み込んでいます。
ただし、FileReader での読み込みは非同期で行われますので、読み込みの完了を loadend イベントを受け取ることによって判断しています。
また、ファイルの内容は reader.result に読み込まれているのですが、reader.result が ArrayBuffer なので直接操作することはできません。そのため、Uint8Array(8 ビット符号なし整数値の配列)に変換しています。
ちなみに blob とは Binary Large Object のことで、一般的に画像や音声などの大きなバイナリデータそのまま格納するためのデータ型です。
<span id="d1"></span><br> <!-- 以下略 --> <script> var d1 = document.getElementById("d1"); // 以下略 var promise = fetch("sample.png", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => { // アロー関数で記述してるが、response => は function(response) と同じ意味 response.blob().then(b => { d1.textContent = b.type; // MIME タイプ d2.textContent = b.size; // バイトサイズ var reader = new FileReader(); reader.addEventListener("loadend", function() { var data = new Uint8Array(reader.result); var x = ""; for (var i = 0 ; i < 16 ; i++) { // 最初の16バイトだけ表示 x += ("0" + data[i].toString(16).toUpperCase()).substr(-2) + " "; // 16進数化 } d3.textContent = x; }); reader.readAsArrayBuffer(b); // ファイルを読み込んで ArrayBuffer に格納する }); }) .catch(err => d1.textContent = err); </script>
ファイルの内容の最初の16バイトだけを表示しています。
画像ファイルを読み込んで img 要素に表示することもできます。
<img id="d1"><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var promise = fetch("sample.png", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => response.blob().then(b => d1.src = URL.createObjectURL(b))) // 画像の URL を得て、img 要素に設定する .catch(err => d2.textContent = err); </script>
画像が表示されます。
ファイルの内容は、arrayBuffer() の返す値の中に Promise インスタンスとして格納されています。ただし、 ArrayBuffer なので直接操作することはできません。そのため、Uint8Array(8 ビット符号なし整数値の配列)に変換しています。
<span id="d1"></span><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var promise = fetch("sample.png", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => { // アロー関数で記述してるが、response => は function(response) と同じ意味 response.arrayBuffer().then(b => { var data = new Uint8Array(b); d1.textContent = data.length; // バイトサイズ var x = ""; for (var i = 0 ; i < 16 ; i++) { // 最初の16バイトだけ表示 x += ("0" + data[i].toString(16).toUpperCase()).substr(-2) + " "; // 16進数化 } d2.textContent = x; }); }) .catch(err => d1.textContent = err); </script>
ファイルの内容の最初の16バイトだけを表示しています。
画像ファイルを読み込んで img 要素に表示することもできます。
<img id="d1"><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var promise = fetch("sample.png", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => { response.arrayBuffer().then(b => { var data = new Uint8Array(b); var binaryData = ""; for (var i = 0, len = data.byteLength ; i < len ; i++) { binaryData += String.fromCharCode(data[i]); } d1.src = "data:image/png;base64," + window.btoa(binaryData); // img 要素に設定する }); }) .catch(err => d2.textContent = err); </script>
画像が表示されます。
ファイルの内容は、json() の返す値の中に Promise インスタンスとして格納されています。ただし、 JSON 形式のオブジェクトなので直接表示することはできません。そのため、すべてのキーを取得してそのキーに対応する値を表示する関数(disp)を作成して表示しています。
注意
JSON ファイルを読み込むためには、JSON という拡張子を Web サーバーの MIME に例えば text/plain などのように登録する必要があります。
<span id="d1" style="white-space:pre;"></span><br> <script> const indent = " "; const delim = ":"; // オブジェクトの表示 function disp(ind, obj) { var keys = Object.keys(obj); // すべてのキー var d = "{\n"; var leading = indent.repeat(++ind); // インデント keys.forEach((key) => { // キーを順番に取り出す var val = obj[key]; // キーに対応する値 if (typeof val === 'object') { d += leading + key + delim + disp(ind, val); // オブジェクトがネストしている } else { var quote = (typeof val === 'string') ? "\"" : ""; // 文字列なら "" で囲む d += leading + key + delim + quote + val + quote + "\n"; // 値 } }); leading = indent.repeat(ind-1); // インデント return d + leading + "}\n"; } var d1 = document.getElementById("d1"); var promise = fetch("sample.json", {method:"get"}); // method:"get" は規定値なので記述しなくても同じ promise .then(response => { // アロー関数で記述してるが、response => は function(response) と同じ意味 response.json().then(j => { d1.textContent = disp(0, j); }); }) .catch(err => d1.textContent = err); </script>
読み込まれた JSON ファイルの内容は j の中にオブジェクトとして読み込まれます。この例ではそのオブジェクトの内容を表示しています。なお、表示する div 要素に white-space:pre 属性を指定しているので、改行されて表示されています。
サーバーにデータを作成するためにパラメーターをリクエストとして送ります。リクエストの送信には、リクエストメソッドとして POST を指定します。
下の例では、サーバーの CGI を呼び出しパラメーターを送信しています。そして、その返答を受け取り表示しています。
ファイルの内容は、text() の返す値の中に Promise インスタンスとして格納されています。
サーバーにパラメーターを文字列として渡すには次のように行います。
application/x-www-form-urlencoded は、フォームに入力されたデータを送信するのための形式です。データは id=data の形式で、データが複数ある場合は & で区切られます(id1=data1&id2=data2)
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); var params = new URLSearchParams(); // サーバーに渡されるパラメーター params.append("name", "Mary"); params.append("gender", "female"); params.append("fruits", "みかん"); fetch("post.cgi", { // オプション method: "post", headers: {"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"}, body: params // name=Mary&gender=female&fruits=みかん }) .then(response => response.text().then(res => d1.innerHTML = res)) .catch(err => d1.textContent = err); </script>
「Mary」さんは「女性」で「みかん」が好きだと送っています。実行例はそれに対する返答です。
なお、上記の例ではパラメーターを URLSearchParams を使用して生成していますが、body のところに直接文字列を記述しても構いません。
body: "name=Mary&gender=female&fruits=みかん"
サーバーにパラメーターを FormData として渡すには次のように行います。
FormData は、マルチパートの情報として送られるようで、multipart/form-data の指定が必要です。したがって、サーバー側でもパラメーターの受け取り方法が変わります。
また、「所持品」のようなチェックボックスは、チェックされたところの value をタブで連結した文字列となるようです。
<form id="favi"> <label>氏名:<input type="text" name="name"></label><br> 性別: <label><input type="radio" name="gender" value="male"> 男</label> <label><input type="radio" name="gender" value="female"> 女</label> <br><br> 所持品: <label><input type="checkbox" name="prop" value="CellPhone" checked="checked"> スマフォ</label> <label><input type="checkbox" name="prop" value="Car"> 自動車</label> <label><input type="checkbox" name="prop" value="Cottage"> 別荘</label> <br> <label>好きな果物: <select name="fruits"> <option value="りんご" label="りんご"></option> <option value="みかん" label="みかん"></option> <option value="バナナ" label="バナナ" selected="selected"></option> <option value="パイナップル" label="パイナップル"></option> </select></label> </form> <span id="d1">何か入力してください。</span><br> <script> var d1 = document.getElementById("d1"); var favi = document.getElementById("favi"); favi.addEventListener("change", send, false); // 入力欄に変更があったら send() を呼び出す function send() { fetch("post.cgi", { // オプション method: "post", headers: {"Content-type": "multipart/form-data; charset=UTF-8"}, body: new FormData(favi) }) .then(response => response.text().then(res => d1.innerHTML = res)) .catch(err => d1.textContent = err); } </script>
下記の欄に適当な入力してください。入力するたび(ただし、「氏名」欄は入力欄から抜けたとき)に返答が変わります。
<input type="file"> で指定されたファイルをアップロードしサーバーに保存します。この例では、ファイルが指定された時点で自動的に送信され、サーバーに保存されます。そして、その保存されたファイル名を受け取り、img タグの src 属性に設定することによって、その画像を表示しています。
<form id="up"> <label>ファイル:<input type="file" name="file" type="file" accept="image/*"></label><br> </form> <img id="d1" style="max-height:200px;max-width:200px;"><br> <div id="d2" style="color:crimson"></div> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var favi = document.getElementById("up"); favi.addEventListener("change", send, false); // 入力欄に変更があったら send() を呼び出す function send() { fetch("upload.cgi", { // オプション method: "post", body: new FormData(favi) }) .then(response => { response.text().then(res => { if (response.ok) { d1.src = res; // 画像 URL d2.textContent = ""; } else { d1.src = ""; d2.innerHTML = res; // エラーメッセージ } }) }) .catch(err => d2.textContent = err); } </script>
画像ファイルを選択すると自動的にアップロードされます。また、ファイルを指定するたびにアップロードされた画像が(大きな画像は縮小されて)表示されます。
アップロードする画像ファイルのサイズは 180KB 以下のものを選んでください。
ヘッダの取得には、リクエストメソッドとして HEAD を指定します。
GET メソッドでも同じように、ヘッダを取得することができますが、GET メソッドはデータのボディ部のデータも一緒に取得してしまうので、データの更新日時(last-modified)などのヘッダー情報のみを取得したい場合は、通信の無駄が発生してしまいます。
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); fetch("sample.txt", {method:"head"}) .then(response => response.headers) .then(headers => { var headerContents = ""; headers.forEach((value, key) => headerContents += key + " : " + value + "<br>"); d1.innerHTML = headerContents; }) .catch(err => d1.textContent = err); </script>
ファイルを読み込んで、ヘッダー情報を表示しています。
headers() の返す値はオブジェクトなので、そのメンバーの名前と値をすべて表示しています。
サーバーへの要求内容を設定します。
fetch の引数としても指定できます。
Request インスタンスを生成する。
引数 url:読み込むファイルの URL あるいは相対パス などを表す文字列
引数 options:オプション(JSON 形式のオブジェクト) fetch 参照
戻り値:Request インスタンス
Request インスタンスの主なプロパティです。
プロパティ | 値 | 説明 | ||||||
---|---|---|---|---|---|---|---|---|
methodR/O | リクエストするメソッド | ○ | ○ | ○ | ○ | ○ | ○ | |
GET | データの取得 | |||||||
POST | データの作成(リクエストの送信) | |||||||
HEAD | ヘッダの取得 | |||||||
PUT | データの作成・置換 | |||||||
DELETE | データの削除 | |||||||
urlR/O | URL | リクエストの URL | ○ | ○ | ○ | ○ | ○ | ○ |
headersR/O | Headers インスタンス | リクエストの Headers インスタンス | ○ | ○ | ○ | ○ | ○ | ○ |
referrerR/O | リファラ | 該当ページに遷移する直前に閲覧されていた遷移元の URL | ○ | ○ | ○ | ○ | ○ | ○ |
URL | 同じオリジンの URL | |||||||
about:client | グローバルのデフォルト | |||||||
(空文字列) | リファラを送信しない | |||||||
referrerPolicyR/O | ポリシー | リファラに関するポリシー | × | × | × | × | × | × |
(空文字列) | 特にリファラに対して条件指定をせず、ブラウザの挙動による | |||||||
no-referrer | リファラを送信しない | |||||||
no-referrer-when-downgrade | HTTPS→HTTP にはリファラを送信しない | |||||||
same-origin | リンク元とリンク先が同一オリジンの場合にリファラを送信する(注) | |||||||
origin | リンク元のオリジンのみを送信する | |||||||
origin-when-cross-origin | リンク元とリンク先が異なるオリジンの場合、リンク元のオリジンのみを送信する 同一オリジンの場合、リンク元の完全な URL をリファラとして送信する | |||||||
strict-origin-when-cross-origin | リンク先が HTTPS の場合、origin-when-cross-origin と同じ | |||||||
unsafe-url | リンク元の完全な URL をリファラとして送信する | |||||||
modeR/O | リクエストのモード | ○ | ○ | ○ | ○ | ○ | ○ | |
cors | クロスオリジンリクエストの許可 | |||||||
no-cors | 他サーバーは HEAD 部の読み取りと GET、POST メソッドのみの許可 | |||||||
same-origin | クロスオリジンリクエストの不可(エラーになる) | |||||||
credentialsR/O | request に使用したい秘密情報 | ○ | ○ | ○ | ○ | ○ | ○ | |
omit | Cookieなど認証情報を付与しない | |||||||
same-origin | 同一ドメインの場合に認証情報を付与する | |||||||
include | 全てのドメインで認証情報を付与する | |||||||
integrityR/O | base64 でエンコードされたハッシュ値 | リクエストの サブリソース完全性を示す値 | × | × | × | × | × | × |
cacheR/O | リクエストのキャッシュモード | ○ | ○ | ○ | ○ | ○ | ○ | |
default | キャッシュ内に新しいものがあれば利用する。 なければ読み込んでキャッシュを更新する | |||||||
no-store | キャッシュが全く無いかのように常に読み込む | |||||||
reload | ネットワークでは、常に読み込んでキャッシュを更新する | |||||||
no-cache | 常に読み込んでキャッシュを更新する | |||||||
force-cache | キャッシュ内にあれば古くても利用する。なければ読み込んでキャッシュを更新する | |||||||
only-if-cache | キャッシュ内にあれば古くても利用する。なければネットワークエラーを返す | |||||||
bodyUsedR/O | Boolean 値 | ボディが既に読み取られた(true) まだ読み取られていない(false) | ○ | ○ | ○ | ○ | ○ | ○ |
(注)オリジン:スキーム、ホスト、ポート番号の組み合わせ
<span id="d01"></span><br> <!-- 以下略 --> <script> var d01 = document.getElementById("d01"); // 以下略 var request = new Request("sample.php", // 実行ファイル { // オプション method: "post", headers: {"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"}, body: "name=Mary&gender=female" }); d01.textContent = request.method; d02.textContent = request.url; var headers = ""; request.headers.forEach(function(value, key) {headers += key + " : " + value + "<br>"}); d03.innerHTML = headers; d04.textContent = request.referrer; d05.textContent = request.referrerPolicy; d06.textContent = request.mode; d07.textContent = request.credentials; d08.textContent = request.integrity; d09.textContent = request.cache; d10.textContent = request.bodyUsed; </script>
サーバーからの返信内容を含んでいます。
Request インスタンスを生成する。
引数 body:返信内容を表す Blob、FormData、URLSearchParams、文字列 など
引数 options:オプション(JSON 形式のオブジェクト) 下記参照
戻り値:Request インスタンス
options には次のようなメンバーがあります。
名前 | 値 | 説明 |
---|---|---|
status | 200 OK 404 Not Found 501 Not Implemented など 詳しくは こちら | HTTP ステータスコード |
statusText | OK、Not Found、Not Implemented など | ステータスコードに対応したステータスメッセージ |
headers | Headers インスタンス | レスポンスに関連した Headers インスタンス |
Response インスタンスの主なプロパティです。
プロパティ | 値 | 説明 | ||||||
---|---|---|---|---|---|---|---|---|
headersR/O | Headers インスタンス | レスポンスの Headers インスタンス | ○ | ○ | ○ | ○ | ○ | ○ |
okR/O | 論理値 | リクエストが成功(status が 200~299)したら true | ○ | ○ | ○ | ○ | ○ | ○ |
redirectedR/O | 論理値 | リクエストがリダイレクトされたら true | ○ | ○ | ○ | ○ | ○ | ○ |
statusR/O | ステータスコード | HTTP ステータスコード(詳しくは こちら) | ○ | ○ | ○ | ○ | ○ | ○ |
statusTextR/O | 文字列 | ステータスコードに対応したステータスメッセージ | ○ | ○ | ○ | ○ | ○ | ○ |
typeR/O | レスポンスタイプ | ○ | ○ | ○ | ○ | ○ | ○ | |
default | 正常(同一サーバーからのレスポンス) | |||||||
cors | 他サーバーからのレスポンス | |||||||
error | ネットワークエラー | |||||||
opaque | 他サーバーへの "no-cors"(HEAD 部の読み取りと GET、POST メソッドのみ)でのレスポンス | |||||||
urlR/O | URL | レスポンス URL | ||||||
bodyUsedR/O | 論理値 | response でボディが使われたかどうかを示す Boolean 値 | ○ | ○ | ○ | ○ | ○ | ○ |
Response インスタンスは、fetch が返してくる情報として使用されることが多いです。fetch は Promise インスタンスを返しますが、Response インスタンスはその中に含まれています。
<span id="d01"></span><br> <!-- 以下略 --> <script> var d01 = document.getElementById("d01"); // 以下略 var response = new Response("ABCDEF", // 返信内容 { // オプション status: 300, statusText: "NG", headers: {"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"} }); var headers = ""; response.headers.forEach(function(value, key) {headers += key + " : " + value + "<br>"}); d01.innerHTML = headers; d02.textContent = response.ok; d03.textContent = response.redirected; d04.textContent = response.status; d05.textContent = response.statusText; d06.textContent = response.type; d07.textContent = response.url; d08.textContent = response.bodyUsed; </script>
この例では、status に 300 を設定していますので、異常終了したということで ok は false になります。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
clone() | なし | Response オブジェクトのクローンを生成する | Response |
error() | なし | ネットワークエラーに関連した新しい Response オブジェクトを返す | Response |
redirect() | なし | 異なる URL で新しい Response オブジェクト を生成する | Response |
Response オブジェクトに含まれる内容は ArrayBuffer、Blob、JSON、プレーンテキストのいずれかの形で受け取ることが出来ます。それらを読み込むには次のメソッドを使用します。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
arrayBuffer() | なし | ボディテキストを ArrayBuffer として解析した結果で解決される Promise を返す | Promise |
blob() | なし | ボディテキストを Blob として解析した結果で解決される Promise を返す | Promise |
formData() | なし | ボディテキストを FormData として解析した結果で解決される Promise を返す | Promise |
json() | なし | ボディテキストを Json として解析した結果で解決される Promise を返す | Promise |
text() | なし | ボディテキストを文字列として解析した結果で解決される Promise を返す | Promise |
text() の例です。
<span id="d1" style="white-space:pre;"></span><br> <script> var d1 = document.getElementById("d1"); var promise = fetch("sample.txt"); promise.then(function(response) { response.text().then(function(t) {d1.textContent = t}); }); </script>
text() の返す値はファイルの内容を持った Promise インスタンスです。ファイルの内容は表示する div 要素に white-space:pre 属性を指定しているので、改行されてすべて表示されています。
その他の使用例は、12.1.1 fetch 命令 (1) データの取得 を参照してください。
Headers インスタンスは、ヘッダ名と値のペアで構成されるリストを持っています。Headers インスタンス自体は、Request インスタンスや Response インスタンスにも含まれますが、そのヘッダ名の構成は異なります。また、ヘッダ名は大文字小文字を区別しません。
Headers オブジェクトは、Map オブジェクトと似たメソッドを持っており、ヘッダ名と値のペアを自由に追加・削除することができます。
Headers インスタンスは、Map オブジェクトに似た次のようなメソッドを持っています。詳しくは、9.6 連想配列(Map) を参照してください。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
append(name,value) | ヘッダ名、値 | ヘッダに新しいヘッダ名を追加する | なし |
delete(name) | ヘッダ名 | ヘッダからヘッダ名を削除する | なし |
entries() | なし | ヘッダ名と値のペアを含むリストを取得する | Iterator インスタンス |
forEach(callback[,thisObject]) | 処理関数、this として使用するインスタンス | 与えられた関数を配列のすべての要素に対して実行する | なし |
get(name) | ヘッダ名 | ヘッダからヘッダ名に対応する値を取得する | 値(文字列) |
has(name) | ヘッダ名 | ヘッダにヘッダ名があるかどうかを検査する | あれば true、なければ false |
keys() | なし | ヘッダに含まれるすべてのヘッダ名のリストを取得する | Iterator インスタンス |
set(name,value) | ヘッダ名、値 | ヘッダ名に対応する値を置き換える | なし |
values() | なし | ヘッダに含まれるすべての値のリストを取得する | Iterator インスタンス |
Headers インスタンスは、ヘッダ名と値のペアで構成されるリストを持っていますが、その内容はインスタンスごとに異なります。
例えば、Response インスタンスに含まれる Headers インスタンスには次のようなプロパティが含まれています。
プロパティ | 値 |
---|---|
accept-rangesR/O | ターゲットリソースに対する範囲要請(bytes:バイト単位での範囲要請をサポート) |
content-lengthR/O | コンテンツのサイズ(単位はバイト) |
content-typeR/O | コンテンツのアプリケーション・メディアタイプ(MIME と同じ) |
dateR/O | 日付 |
etagR/O | エンティティタグ。リソースの全体や一部を特定する固有値 |
last-modifiedR/O | コンテンツの最終更新時刻 |
serverR/O | HTTPサーバアプリケーション種類を示す固有テキスト値 |
新規に生成しプロパティを個別に追加した Headers インスタンスと、Response インスタンスに含まれる Headers インスタンスの両方について、プロパティのヘッダ名と値のペアを表示しています。
<script> <span id="d01"></span><br> <span id="d02"></span><br> <script> var d01 = document.getElementById("d01"); var d02 = document.getElementById("d02"); var headersObj = new Headers(); headersObj.append("String", "ABC"); headersObj.append("Number", 12); var headers = ""; headersObj.forEach(function(value, key) {headers += key + " : " + value + "<br>"}); d01.innerHTML = headers; fetch("sample.txt", {method:"get"}).then(function(response) { var headers = ""; response.headers.forEach(function(value, key) {headers += key + " : " + value + "<br>"}); d02.innerHTML = headers; }) .catch(err => d02.textContent = err); </script>
ヘッダ名は大文字で指定しても小文字で登録されるようです。
非同期処理の最終的な完了処理(もしくは失敗)およびその結果の値を表現します。
Promise の基本的な考え方は「非同期的に動作する関数は、受信したデータやファイルから読み込んだデータなどの、本来返したい戻り値の代わりに『Promise』という特別なオブジェクトを返しておき、(本来返したい)値を渡せる状態になったら、その Promise オブジェクトを通して呼び出し元に値を渡す」というものです。
非同期的に動作する代表的な関数が fetch です。
Promise インスタンスの持つメソッドについて説明します。
新規の Promise インスタンスを生成します。
Promise インスタンスの状態は以下のいずれかになります。
Promise インスタンスを生成する。
引数 2つの関数 resolve と reject を引数とする関数
(この関数は非同期の処理を開始して、成功した場合は resolve を、失敗した場合は reject を呼び出すようにする)
例えば、XMLHttpRequest を使用してファイルを読み込む処理を考えてみます。XMLHttpRequest はファイルの読み込みを非同期に行いますので、ファイル読み込みの完了はイベント(この例では readystatechange)によって知らされます。
次の例は、ファイル xxxxx.txt を読み込んで、その文字数を表示するものです。
<span id="d1"></span><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); class MyFileReader { constructor(filename) { this.filename = filename; this.callback = null; this.length = 0; this.req = new XMLHttpRequest(); // HTTPでファイルを読み込むためのXMLHttpRrequestオブジェクトを生成 this.req.open("get", this.filename, true); // アクセスするファイルを指定 } read(callback) { var self = this; this.callback = callback; this.req.send(null); // HTTPリクエストの発行 this.req.onreadystatechange = function(){ if (self.req.readyState == 4) { if (self.req.status == 200) { self.length = self.req.responseText.length; // ファイルサイズ self.callback(); } else if (self.req.status == 404) { throw self.filename + " not found."; } } } } size() { return this.length; } } function execute() { d2.textContent = reader.size(); // 文字数 ==> d2 -----② } var filename = "xxxxx.txt"; var reader = new MyFileReader(fileName); reader.read(execute); // ファイルを読み込む d1.textContent = reader.size(); // 文字数 ==> d1 -----① </script>
XMLHttpRequest を使用した場合、ファイルは一度にすべて読み込まれ、responseText に保存されます。したがって、d1(①のところ)には読み込まれた文字数が設定されてもよさそうですが、実行結果としては 0 になっています。これは、ファイルの読み込みが非同期で行われているため、ファイルを読み込み終わる前に ① の命令が実行されてしまうからです。
これに対して、ファイルの読み込みが完了したときに(read からコールバックされる execute 内で)設定している d2(②のところ)には読み込んだ文字数が設定されています。
それでは同じ処理を Promise を使用して記述してみます。
resolve の引数は返したい値、reject の引数はエラー理由を設定した、文字列や数値や Error あるいはそのサブクラスのインスタンスです。
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); class MyFileReader { constructor(filename){ return new Promise(function(resolve, reject){ var req = new XMLHttpRequest(); // HTTPでファイルを読み込むための XMLHttpRrequest インスタンスを生成 req.open("get", filename, true); // アクセスするファイルを指定 req.addEventListener("loadend", function(e){ // loadend イベントはリクエストが成功した場合も失敗した場合も呼び出される if (req.readyState == 4 && req.status == 200) { resolve(req.responseText); // fulfilled return; // return はなくてもよい(この後 reject を実行しても fulfilled の状態は変わらない) } reject(new Error("error")); // rejected }); req.send(null); }); } } var filename = "xxxxx.txt"; var promise = new MyFileReader(filename); promise.then( function(text){d1.textContent = text.length;}, //成功した場合(文字数) -----① function(err){d1.textContent = err.message;} //失敗した場合 ); </script>
MyFileReader は、ファイルの読み込みが正常に終了したのならば resolve を呼び、読み込んだファイルの内容を返し、そして、失敗したのならば reject を呼び、"error" という文字列を持つ Error インスタンスを返す Promise インスタンスを生成し、戻り値としています。
そして、これらの値が then の引数として指定された関数に渡されます。成功したときが一つ目の関数、失敗したときが二つ目の関数です。
then() メソッドは Promise を返します。最大2つの引数、Promise が成功した場合と失敗した場合のコールバック関数を取ります。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
then(func1[, func2]) | Promise を返す関数 | Promise インスタンスに実装した関数の成否によって処理を行う。成功した場合一つ目の関数、失敗した場合は二つ目の関数が実行される。 | Promise インスタンス |
先の例と同じものです。ここですでに then が使用されていました。
この例では MyFileReader が成功すれば resolve を呼び出し(─→ ① のところ)、その引数の req.responseText の内容が then の一つ目の関数(←── ① のところ)の text に設定され、一つ目の関数が実行されます。
もし失敗すれば reject を呼び出し(─→ ② のところ)、その引数の Error インスタンスが then の二つ目の関数(←── ② のところ)の err に設定され、二つ目の関数が実行されます。
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); class MyFileReader { constructor(filename){ return new Promise(function(resolve, reject){ var req = new XMLHttpRequest(); // HTTPでファイルを読み込むための XMLHttpRrequest インスタンスを生成 req.open("get", filename, true); req.addEventListener("loadend", function(e){ if (req.readyState == 4 && req.status == 200) { resolve(req.responseText); //────────→ ① return; } reject(new Error("error")); //────────→ ② }); req.send(null); }); } } var filename = "xxxxx.txt"; var promise = new MyFileReader(filename); promise.then( function(text){d1.textContent = text.length;}, //←── ① (成功したとき) function(err){d1.textContent = err.message;} //←── ② (失敗したとき) ); </script>
then は Promise インスタンスを返すので、then をいくつも続けて書くことができます。このとき、then に記述した関数で値を返すと、その値が次の then に記述した関数の引数になります。なお、値を返さなければ undefined が引数値になります。
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); var promise = Promise.resolve(); // new Promise(function(resolve) {resolve();}) と同じ意味 promise .then(function(){return "one";}) .then(function(arg){return arg + " two";}) // arg = "one" .then(function(arg){d1.textContent = arg;}); // arg = "one two" </script>
戻り値が次の関数に引数として渡されます。
then の中で Promise を返す場合は、次のようになります。
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); var promise = Promise.resolve(); // new Promise(function(resolve) {resolve();}) と同じ意味 promise .then(function(){return new Promise(function(resolve, reject){resolve("one")});}) .then(function(arg){return new Promise(function(resolve, reject){resolve(arg + " two")});}) // arg = "one" .then(function(arg){d1.style.color="blue"; d1.textContent = arg;}); // arg = "one two" .catch(function(err){d1.style.color="red"; d1.textContent = err;}); // 実行されない </script>
catch() メソッドは Promise を返し、then の途中で失敗した場合のコールバック関数を取ります。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
catch(func) | Promise を返す関数 | Promise インスタンスに実装した関数が失敗したときの戻り値を得るために、引数で指定した関数が実行される。 then(undefined, func) と同じ | Promise インスタンス |
then の途中で失敗した場合は、catch で指定した関数が実行されます。
<span id="d1"></span><br> <script> var d1 = document.getElementById("d1"); var promise = Promise.resolve(); // new Promise(function(resolve) {resolve();}) と同じ意味 promise .then(function(){return new Promise(function(resolve, reject){resolve("one")});}) .then(function(arg){return new Promise(function(resolve, reject){reject("error")});}) // 失敗 .then(function(arg){d1.style.color="blue"; d1.textContent = arg;}); // 実行されない .catch(function(err){d1.style.color="red"; d1.textContent = err;}); </script>
Promise オブジェクトの持つメソッドについて説明します。
all メソッドは、引数に指定した全ての Promise を実行します。そして、全ての Promise が成功した(fulfilled)か、または1つでも失敗した(rejected)場合に処理が終了します。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
all(list) | 配列、リスト | すべてを並列処理し、すべての結果を返す。 | なし |
<span id="d1"></span><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var p1 = new Promise(function(resolve) {setTimeout(resolve, 4000, "one");}); // 4秒後に resolve("one") を実行 var p2 = new Promise(function(resolve) {setTimeout(resolve, 1000, "two");}); // 1秒後に resolve("two") を実行 var p3 = new Promise(function(resolve) {setTimeout(resolve, 3000, "three");}); // 3秒後に resolve("three") を実行 var px = new Promise(function(resolve, reject) {setTimeout(reject, 2000, "error");}); // 2秒後に reject("error") を実行 Promise.all([p1, p2, p3]).then( // すべて成功 ---① function(value) {d1.textContent = value;}, // 成功したときに実行される関数 --- ①-1 function(reason) {d1.textContent = reason;}); Promise.all([p1, p2, px]).then( // 失敗 ---② function(value) {d2.textContent = value;}, function(reason) {d2.textContent = reason;}); // 失敗したときに実行される関数 --- ②-1 </script>
Promise.all は、配列内で指定された関数が並列で実行されます。そして、すべてが成功する ① の場合だと4秒後に then の一つ目の引数で指定された関数(①-1)が実行されます。また、px が失敗する ② の場合では2秒後に二つ目の引数で指定された関数(②-1)が実行されます。
race メソッドは、最初に成功(fulfilled)もしくは失敗(rejected)した promise の返した値を引数に、resolve を実行します。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
race(list) | 配列、リスト | 並列処理を行い、どれかが成功か失敗したら、その結果を返す。 | なし |
<span id="d1"></span><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var p1 = new Promise(function(resolve) {setTimeout(resolve, 4000, "one");}); // 4秒後に resolve("one") を実行 var p2 = new Promise(function(resolve) {setTimeout(resolve, 1000, "two");}); // 1秒後に resolve("two") を実行 var p3 = new Promise(function(resolve) {setTimeout(resolve, 3000, "three");}); // 3秒後に resolve("three") を実行 var px = new Promise(function(resolve, reject) {setTimeout(reject, 2000, "error");}); // 2秒後に reject("error") を実行 Promise.race([p1, p2, p3]).then( // 最初に p2 が成功 ---① function(value) {d1.textContent = value;}, // 成功が最初の結果だったときに実行される関数 ---①-1 function(reason) {d1.textContent = reason;}); Promise.race([p1, p3, px]).then( // 最初に px が失敗 ---② function(value) {d2.textContent = value;}, function(reason) {d2.textContent = reason;}); // 失敗が最初の結果だったときに実行される関数 ---②-1 </script>
Promise.race は、配列内で指定された関数が並列で実行されます。そして、先にどれか(①の例だと1秒後に p2)が成功し、即座に then の一つ目の引数で指定された関数(①-1)が実行されます。また、先にどれか(②の例だと2秒後に px)が失敗した場合には即座に then の二つ目の引数で指定された関数(②-1)が実行されます。
resolve メソッドは引数で与えられた値で解決(fulfilled)された Promise オブジェクトを返します。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
resolve([value]) | 文字列、Promise など | 引数で与えられた値で完了した Promise インスタンスを返す。 | Promise インスタンス |
Promise.resolve() は次のように記述したものと同じ意味になります。
new Promise(function(resolve){resolve("success");}); // Promise.resolve("success"))
<span id="d1"></span><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var promise = Promise.resolve("success"); // 成功 promise.then( function(value) {d1.textContent = "resolve " + value;}, // 成功だったときに実行される関数 function(value) {d2.textContent = "reject " + value;} ); </script>
reject メソッドは、引数で与えられた理由で失敗(rejected)した Promise オブジェクトを返します。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
reject([reason]) | 文字列、Error など | 引数で与えられた値を理由に失敗した Promise インスタンスを返す。 | Promise インスタンス |
Promise.reject() は、次のように記述したものと同じ意味になります。
new Promise(function(resolve, reject){reject("fail");}); // Promise.reject("fail"))
<span id="d1"></span><br> <span id="d2"></span><br> <script> var d1 = document.getElementById("d1"); var d2 = document.getElementById("d2"); var promise = Promise.reject("fail"); // 失敗 promise.then( function(value) {d1.textContent = "resolve " + value;}, function(value) {d2.textContent = "reject " + value;} // 失敗だったときに実行される関数 ); </script>
async、await は、Promise による非同期処理をより簡潔に効率よく記述できます。
async 宣言は、Promise オブジェクトを返す 非同期関数 を定義します。async を関数の前につけると、その関数は Promise を返すようになるわけです。
つまり、async を付けた関数(以降、async 関数という)の戻り値に対して then() で結果を受け取ることができ、catch() でエラー処理を行うことができるようになるわけです。
Promise を返す非同期関数を定義する。
戻り値:処理が成功して完了した(fulfilled)Promise
async 関数の戻り値は、処理が成功して完了した Promise なので、then() に記述された関数の引数として値を受け取ることができます。
<div id="d1">0</div> <div id="d2"></div> <script> async function asyncTwice(value) { return value * 2; } function twice(value) { asyncTwice(value).then(function(arg) { d1.textContent = arg; // ① 非同期に動作しているが、asyncTwice が完了してから代入される }); d2.textContent = d1.textContent; // ② 非同期に動作しているので、d1 に代入される前に実行される } twice(5); </script>
asyncTwice は非同期に動くので、asyncTwice が終了する前に次の命令である d1 から d2 への代入が行われてしまいます。そのため、最初から d1 に設定されていた 0 が d2 に入ることになります。
上記の例で定義した asyncTwice は Promise を利用すると次のようになります。
function asyncTwice(value) { return new Promise(function(resolve) { resolve(value * 2) }); }
async 関数は、await 式を含むことができます。await 式は、async 関数の実行を一時停止し、 Promise の解決を待ちます。そして async 関数の実行を再開し、解決された値を返します。
式が Promise オブジェクトのときは、Promise が確定しその結果を返すまで、JavaScript を待機する。式が Promise オブジェクトではない場合はその値自体を返す。
await は async 関数の中でのみ使用できる。
戻り値:解決された Promise オブジェクト の値
await 宣言は、async 関数の中でのみ有効です。async 関数の外で使用した場合はエラーになります。
async 関数の中では await を使うことで .then() を代用できます。
<div id="d1">0</div> <div id="d2"></div> <script> async function asyncTwice(value) { return value * 2; } async function twice(value) { d1.textContent = await asyncTwice(value); // ① 非同期に動作しているが、asyncTwice が完了してから代入される d2.textContent = d1.textContent; // ② asyncTwice の戻り値が d1 に代入された後に実行される } twice(5); </script>
asyncTwice は非同期に動きますが、await が記述されていますので、代入は asyncTwice が終了するのを待ってから行われます。
fetch を使用した例は次のようになります。
<div id="d1">0</div> <div id="d2"></div> <script> async function readText(filename) { let response = await fetch(filename); if (response.ok) return await response.text(); else return response.status; } async function read(filename) { d1.textContent = await readText(filename); // ① 非同期に動作しているが、readText が完了してから代入される d2.textContent = d1.textContent; // ② readText の戻り値が d1 に代入された後に実行される } read("sample.txt"); </script>
なお、Javascript には、一定時間処理を停止するような関数が存在しません。
処理を一時停止させたり、待ちを発生させたい場合に、よく行う方法としては、setTimeout 関数を利用する方法があります。
async、await を使用すると次の例のようになります。
<div id="d"></div> <script> function sleep(msec) { return new Promise(function(resolve) { setTimeout(resolve, msec); }); } async function countDown(n) { d.textContent = n; while(n > 0) { await sleep(1000); d.textContent = --n; } } countDown(10); </script>
次の例は、1秒ごとに文字を表示する(はずの)disp 関数です。
しかし、Array オブジェクトの forEach や map では、1秒ごとに表示されるのではなく、一度に表示されてしまいます。
let list = ['a', 'b', 'c', 'd', 'e']; function disp() { list.forEach(async function(v) { await sleep(1000); d.textContent += v; }); } disp();
これは、forEach で関数が5回あっという間に呼ばれ、呼ばれた関数それぞれが1秒待って文字を表示するからで、結果として1秒後にすべての文字が一度に表示されることになるからです。
for 文にすれば、1秒ごとに表示されます。
let list = ['a', 'b', 'c', 'd', 'e']; async function disp() { for (let i in list) { await sleep(1000); d.textContent += list[i]; }); } disp();
ブラウザ上でサーバーとHTTP通信を行うためのAPIです。
ページ全体を更新する必要なしに、データを受け取ることができます。これでユーザーの作業を中断させることなく、ウェブページの一部を更新することができます。XMLHttpRequest は AJAX プログラミングで頻繁に使用されます。
XMLHttpRequest という名前ではありますが、XML だけでなくあらゆる種類のデータを受け取るために使用することができ、HTTP 以外の file や ftp のプロトコルにも対応しています。
HTTP リクエストを送るには、XMLHttpRequest オブジェクトを作成し、URL を開いてリクエストを送信します。トランザクションが完了すると、インスタンスには結果の HTTP ステータスコードやレスポンスの本文などの有益な情報が格納されます。
新規の XMLHttpRequest インスタンスを生成します。
XMLHttpRequest インスタンスを生成する。
引数 なし
<script> var req = new XMLHttpRequest(); // HTTPでファイルを読み込むためのXMLHttpRrequestオブジェクトを生成 </script>
プロパティ | 値 | 説明 |
---|---|---|
readyStateR/O | リクエストの状態 | |
UNSENT (0) | クライアントは作成済み。open() はまだ呼ばれていない。 | |
OPENED (1) | open() が呼び出し済み。 | |
HEADERS_RECEIVED (2) | send() が呼び出し済みで、ヘッダーとステータスが利用可能。 | |
LOADING (3) | ダウンロード中。responseText には部分データが入っている。 | |
DONE (4) | 操作が完了した。 | |
responseR/O | 文字列 | レスポンスの内容。リクエストが完了していない場合は null(responseType の値と、内容が一致しない場合、nullを返すことがある) |
responseTextR/O | 文字列 | レスポンスの内容 |
responseType | レスポンス型 | |
"" | 型は自動判別される規定値 | |
"arraybuffer" | 型は ArrayBuffer | |
"blob" | 型は Blob(Binary Large Object) 一般的に画像や音声などの大きなバイナリデータそのまま格納するためのデータ型) | |
"document" | 型は Document(HTML や XML と解釈できない場合は null) | |
"json" | 型は JSON(JSON と解釈できない場合は null) | |
"text" | 型は 文字列 | |
responseURLR/O | URL | レスポンスがあったURL |
responseXMLR/O | XMLDocument インスタンス | レスポンスの内容 |
statusR/O | 数値 | ステータス番号(例えば "Not Found" は "404"、"OK" は "200") 詳しくは こちら |
statusTextR/O | 文字列 | ステータスのメッセージ(例えば "Not Found"、"OK") |
timeout | ミリ秒 | リクエストが自動的に終了するまでの時間(規定値:0) タイムアウトとなった場合、timeoutイベントが発生する。 |
uploadR/O | XMLHttpRequestUpload インスタンス | アップロード状況 |
withCredentialsR/O | 論理値 | リクエストにcookieなどの認証情報を含める(true)か否か(false) |
readyState の状態が変わるごとに readystatechange イベントが発生しプロパティが変化します。
次の例では5回呼び出されていて、その時のプロパティ値を表示しています。
<span id="d11"></span><span id="d12"></span> <!-- 略 --> <br> <!-- 以下略 --> <script> var req = new XMLHttpRequest(); // HTTPでファイルを読み込むためのXMLHttpRrequestオブジェクトを生成 var m = 1; req.onreadystatechange = function() { document.getElementById("d1" + m).textContent = req.status; document.getElementById("d2" + m).textContent = req.statusText; document.getElementById("d3" + m).textContent = req.readyState; document.getElementById("d4" + m).textContent = req.response; document.getElementById("d5" + m).textContent = req.responseText; document.getElementById("d6" + m).textContent = req.responseType; document.getElementById("d7" + m).textContent = req.responseURL; document.getElementById("d8" + m).textContent = req.responseXML; m++; }; req.onreadystatechange(); req.open("get", "sample.txt", true); // アクセスするファイルを指定 req.responseType = ""; req.send(); </script>
受診内容は responseType によって次のように異なります。
<span id="d10"></span><span id="d11"></span> <!-- 略 --> <br> <!-- 以下略 --> <script> var m = 0; var req = new XMLHttpRequest(); // HTTPでファイルを読み込むためのXMLHttpRrequestオブジェクトを生成 req.onreadystatechange = function() { if (req.readyState != 4) return; document.getElementById("d3" + m).textContent = req.responseType; document.getElementById("d4" + m).textContent = req.responseURL; switch(req.responseType) { case "arraybuffer": document.getElementById("d1" + m).innerHTML = req.response.byteLength; break; case "blob": document.getElementById("d1" + m).innerHTML = req.response.type + req.response.size; break; case "document": document.getElementById("d1" + m).innerHTML = req.response.contentType; document.getElementById("d5" + m).textContent = req.responseXML.documentElement.outerHTML; break; case "json": document.getElementById("d1" + m).innerHTML = dispJSON(req.response); break; case "text": document.getElementById("d1" + m).textContent = req.response; document.getElementById("d2" + m).textContent = req.responseText; break; } if (++m < func.length) func[m](); }; var func = [func1, func2, func3, func4, func5]; func[m](); function func1() { req.open("get", "data/sample.png", true); req.responseType = "arraybuffer"; req.send(); } function func2() { req.open("get", "data/sample.png", true); req.responseType = "blob"; req.send(); } function func3() { req.open("get", "data/sample.xml", true); req.responseType = "document"; req.send(); } function func4() { req.open("get", "data/sample.json", true); req.responseType = "json"; req.send(); } function func5() { req.open("get", "data/sample.txt", true); req.responseType = "text"; req.send(); } function dispJSON(obj) { var s = ""; var k = Object.keys(obj); for (var i in k) { s += k[i] + ":" + obj[k[i]] + "<br>"; } return s; } </script>
response は responseType によって次のように異なります。text 以外はオブジェクトが返ってきますので、この例ではその一部やオブジェクトを文字列変換して表示しています。
新しく作成されたリクエストを初期化したり、既存のリクエストを再初期化したりします。
ただし、すでに有効なリクエスト(すでに open() が呼び出されたもの)に対してこのメソッドを実行すると、abort() を実行したのと同じことになります。
メソッド | 引数 | 機能 | 戻り値 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
open(httpメソッド, アクセス先 [, 非同期 [, ユーザ名[, パスワード]]]) | httpメソッド,
非同期的(true)同期的(false),(規定値:true) 認証時のユーザ名,(規定値:null) 認証時のパスワード(規定値:null) | 新しく作成されたリクエストを初期化したり、既存のリクエストを再初期化したりする。 | なし |
httpメソッドを指定することにより、行いたい処理をサーバに伝えることができます。この例では、GET、POST、HEAD を指定しています。
なお、この例では、通信は同期的に行っていて send() メソッドはレスポンスを受信するまで戻りません。
<span id="d1"></span><br> <!-- 以下略 --> <script> function get(url, data) { var xhr = new XMLHttpRequest(); xhr.open('GET', url + "?" + data, false); xhr.send(null); return xhr.responseText; } function post(url, data) { var xhr = new XMLHttpRequest(); xhr.open('POST', url, false); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(data); return xhr.responseText; } function head(url) { var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, false); xhr.send(); return xhr.getResponseHeader("date") + " " + xhr.getResponseHeader("content-type"); } d1.innerHTML = get("cgi/get.cgi", "name=太郎&gender=male"); d2.innerHTML = post("cgi/post.cgi", "name=花子&gender=female"); d3.innerHTML = head("cgi/get.cgi"); </script>
通信を非同期的に行った場合には、レスポンスを受信する前に send() メソッドを抜けて次の命令を実行してします。
しかし、受信の完了は、イベントによって知ることができます。
<span id="d1"></span><br> <span id="d2"></span><br> <script> function get(url, data) { var xhr = new XMLHttpRequest(); xhr.onload = function() {d2.innerHTML = xhr.responseText;} // load イベント後の responseText 内容(受信している) xhr.open('GET', url + "?" + data, true); xhr.send(null); return xhr.responseText; // send 直後の responseText 内容(まだ受信していない) } d1.innerHTML = get("cgi/get.cgi", "name=太郎&gender=male"); </script>
send() 直後の responseText には何も入っていませんが、load イベント後の responseText には受信した内容が設定されています。
サーバへリクエストを送信したり、すでに送信された要求を中止します。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
send([データ]) | 送信データ(POST リクエストの場合)(規定値:null) | サーバへリクエストを送信する。 | なし |
abort() | なし | 送信された要求を中止する。 | なし |
Content-Type と送信された内容を表示しています。
ただし、Blob のときは Content-Type はなく、送信された内容は画像データの内容そのままです。この例では、それを base64 形式に変換して表示しています。なお、canvas 要素の画像の Blob への変換は、「11.12 画像出力」を参照してください。
また、Document は内容が多いので ... とし、それ以降を省略して表示しています。
<span id="d1"></span><br> <span id="d2"></span><br> <span id="d3"></span><br> <img id="d4"><br> <form form="form"> name: <input name="name" value="小島"> address: <input name="addr" value="茅ヶ崎"> </form> <span id="d5"></span><br> <script> const CGI = "cgi/xmlhttprequest.pl"; var xhr = new XMLHttpRequest(); func1(); function func1() { xhr.open("POST", CGI, true); xhr.onload = function() { document.getElementById("d1").textContent = xhr.response; func2();}; xhr.send("普通の文字列") ; } function func2() { xhr.open("POST", CGI, true); xhr.onload = function() { document.getElementById("d2").textContent = xhr.response; func3();}; var urlSearchParams = new URLSearchParams("name=小島&addr=茅ヶ崎"); xhr.send(urlSearchParams); } function func3() { xhr.open("POST", CGI, true); xhr.onload = function() { document.getElementById("d3").textContent = xhr.response.substring(0, 200) + " ..."; func4();}; xhr.send(document); } function func4() { xhr.open("POST", CGI, true); xhr.responseType = 'blob'; xhr.onload = function() { var file = new Blob([xhr.response], {'type':'image/png'}); var reader = new FileReader(); reader.onload = function(event) { var arr = new Uint8Array(reader.result); // ③サーバーから送り返された Blob を base64 形式に変換して表示する var b = ""; for(var i = 0, len = arr.length ; i < len ; i++) { b += String.fromCharCode(arr[i]); } document.getElementById("d3").src = "data:image/png;base64," + window.btoa(b); func5(); } reader.readAsArrayBuffer(file); }; var image = new Image(); image.onload = function() { var canvas = document.createElement("canvas"); canvas.height = 80; var context = canvas.getContext("2d") ; context.drawImage(image, 0, 0, 74, 74); if (canvas.toBlob != undefined) // ②イメージが読み込まれたら Blob に変換しサーバーに送信する canvas.toBlob(function(result) { xhr.send(result); }); else xhr.send(canvas.msToBlob()); }; image.src = "figures/bear.png"; // ①イメージを設定する } function func5() { xhr.open("POST", CGI, true); xhr.onload = function() { document.getElementById("d5").textContent = xhr.response;}; var formData = new FormData(document.getElementById("form")); xhr.send(formData) ; } </script>
Document の例でエラーが発生しているかもしれませんが、サーバーの設定の問題かもしれません。
abort() を実行すると、送信が中断します。
<span id="d1"></span><br> <script> var xhr = new XMLHttpRequest(); xhr.open("POST", "cgi/xmlhttprequest.cgi", true); xhr.onprogress = function() {xhr.abort();}; xhr.onabort = function() {document.getElementById("d1").textContent = "中断(" + xhr.readyState + ")";}; xhr.onload = function() {document.getElementById("d1").textContent = "完了(" + xhr.readyState + ")";}; xhr.send("普通の文字列") ; </script>
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
getAllResponseHeaders() | なし | すべてのヘッダー名を含む文字列を返す。 | すべてのヘッダー名とその内容 |
getResponseHeader(ヘッダー名) | 要求するヘッダーの名前 | 指定したヘッダー名を含む文字列を返す。 | ヘッダー名とその値 |
setResponseHeader(ヘッダー名, 値) | 設定するヘッダーの名前、設定値 | 指定したヘッダーに値を設定する。 | なし |
<span id="d1"></span><br> <script> var xhr = new XMLHttpRequest(); xhr.addEventListener('load', function(){ document.getElementById("d1").textContent = xhr.getAllResponseHeaders(); document.getElementById("d2").textContent = xhr.getResponseHeader("Content-Type"); }, false); xhr.open("GET", "data/sample.txt", true); xhr.send(); </script>
Content-Type は、POST や PUT などの送信において、クライアントからサーバーにどのような種類のデータが実際に送られたかを伝えます。それが正しく設定されていないと適切な処理がされません。
<span id="d1"></span><br> <script> var xhr1 = new XMLHttpRequest(); xhr1.onload = function() { document.getElementById("d1").innerHTML = xhr1.responseText; }; xhr1.open("POST", "cgi/post.cgi", true); xhr1.setRequestHeader("Content-Type", "text/plain;"); // プレーンテキスト xhr1.send("name=太郎&gender=male"); var xhr2 = new XMLHttpRequest(); xhr2.onload = function() { document.getElementById("d2").innerHTML = xhr2.responseText; }; xhr2.open("POST", "cgi/post.cgi", true); xhr2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); // Webサーバーへの POST送信 xhr2.send("name=太郎&gender=male"); </script>
FileReader オブジェクトを使うと、ユーザーのコンピューター内にあるファイル (もしくはバッファ上の生データ) をウェブアプリケーションから非同期的に読み込むことが出来ます。
読み込むファイルやデータは File ないし Blob オブジェクトとして指定します。
新規の FileReader インスタンスを生成します。
FileReader インスタンスを生成する。
引数 なし
<script> var req = new FileReader(); // ファイルを読み込むためのFileReaderオブジェクトを生成 </script>
プロパティ | 値 | 説明 |
---|---|---|
errorR/O | DOMException インスタンス | ファイルの読込中に生じたエラー |
readyStateR/O | FileReader の状態を表す数値 | |
EMPTY (0) | まだデータは何も読み込まれていない | |
LOADING (1) | データの読み込み中 | |
DONE (2) | 読込処理がすべて終了した | |
resultR/O | 文字列 | 読み込んだファイルの内容 |
Blobオブジェクトをテキスト文字列として取得してみます。
<span id="d01"></span><span id="d02"></span> <!-- 略 --> <br> <!-- 以下略 --> <script> function disp(message){ document.getElementById("d0" + m).textContent = message; document.getElementById("d1" + m).textContent = reader.readyState; document.getElementById("d2" + m).textContent = reader.result; document.getElementById("d3" + m).textContent = reader.error; m++; }; var m = 1; var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onloadstart = function() {disp("loadstart");}; reader.onprogress = function() {disp("progress");}; reader.onload = function() {disp("load");}; reader.onloadend = function() {disp("loadend");}; reader.onerror = function() {disp("error");}; disp(""); var blob = new Blob(["文字列の取得"], {type:"text/plain"}); // Blob オブジェクトを作成 reader.readAsText(blob); </script>
読込処理を中断させた場合は次のようになります。
メソッド | 引数 | 機能 | 戻り値 |
---|---|---|---|
abort() | なし | 読込処理を中断し、readyState を DONE にする | なし |
readAsArrayBuffer(blob) | Blob か File オブジェクト | Blob や File オブジェクトの内容を読み込み、readyState を DONE にする。 result プロパティにはファイルのデータを表す ArrayBuffer が格納される。 | なし |
readAsBinaryString(blob) | Blob か File オブジェクト | Blob や File オブジェクトの内容を読み込み、readyState を DONE にする。 result プロパティには生のバイナリデータを文字列で解釈したものが格納される。 | なし |
readAsDataURL(blob) | Blob か File オブジェクト | Blob や File オブジェクトの内容を読み込み、readyState を DONE にする。 result プロパティには base64 エンコーディングされた URL の文字列が格納される。 | なし |
readAsText (blob[, encoding]) | Blob か File オブジェクト エンコード(規定値:UTF-8) | Blob や File オブジェクトの内容を読み込み、readyState を DONE にする。 result プロパティにはファイルの内容がテキストとして格納される。 | なし |
<span id="d01"></span><span id="d02"></span><br> <!-- 以下略 --> <script> var blob = new Blob(["文字列の取得"], {type:"text/plain"}); // Blob オブジェクトを作成 function readAsArrayBuffer() { var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onload = function() { d01.textContent = reader.result.byteLength; var data = new Uint8Array(reader.result); data.forEach(d => d02.textContent += d.toString(16).toUpperCase() + " "); }; reader.readAsArrayBuffer(blob); } function readAsBinaryString() { var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onload = function() { d11.textContent = btoa(reader.result); }; reader.readAsBinaryString(blob); } function readAsDataURL() { var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onload = function() { d21.textContent = reader.result; }; reader.readAsDataURL(blob); } function readAsText() { var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onload = function() { d31.textContent = reader.result; }; reader.readAsText(blob); } readAsArrayBuffer(); readAsBinaryString(); readAsDataURL(); readAsText(); </script>
abort() によって、読み取り操作を中止させると次のようになります。
<span id="d01"></span><br> <!-- 以下略 --> <script> function disp(message){ document.getElementById("d01").textContent = message; document.getElementById("d11").textContent = reader.readyState; document.getElementById("d21").textContent = reader.result; document.getElementById("d31").textContent = reader.error; }; var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onloadend = function() {disp("loadend");}; reader.onerror = function() {disp("error");}; var blob = new Blob(["文字列の取得"], {type:"text/plain"}); // Blob オブジェクトを作成 reader.readAsText(blob); reader.abort(); // 読み取り操作を中止させる </script>
File オブジェクトを使用した例は次のようになります。
<input id="file" type="file" accept="text/plain"> エンコード:<select id="encoding"> <option value="Shift_JIS">シフトJIS</option> <option value="UTF-8">UTF-8</option> </select><br> <span id="d"></span><br> <script> var reader = new FileReader(); // FileReader オブジェクトを作成 reader.onload = function() { d.textContent = reader.result; } file.onchange = function(e) { reader.readAsText(e.target.files[0], encoding.value); } </script>