🐷

fetch 入門

2023/01/20に公開

json を取得するまで。

fetch とは

Webサーバからリソースを取得する工程を開始して、promise オブジェクトを返す。

fetch を使用してみる

CodePen が手軽で確認しやすい。
https://codepen.io/

console.log()で結果を確認していく。

fetch() の戻り値

const promise = fetch('https://weather.tsukumijima.net/api/forecast/city/250010');

console.log(promise);

// 結果: Promise オブジェクトが返される.
Promise {<pending>}

pending は状態を示している。
そのままで待機中の状態を指している。

サイトによっては読み取り処理が進行中とも書かれている。

Promise の中を確認

Promise をクリックして中身を確認してみる。
[[PromiseState]]: "fulfilled" が確認できる。
fulfilled は非同期処理が成功しているときの状態。

次に引数のURLを適当なもの(http://localhost)に変更すると
[[PromiseState]]: "rejected" が確認できる。
rejected は非同期処理が失敗しているときの状態。

pending から fulfilles または rejected に状態が変化する。

ハンドラーを使用して更に処理をすすめる

Promise の状態によって呼び出される関数が変わる。

成功(fulfilled)であれば then()ハンドラーが呼び出され、
失敗(rejected)であれば catch()ハンドラーが呼び出される。

それぞれ確認してみる。

成功バージョン

then()メソッドを使用する。
使用するとthen()の引数に Response オブジェクトが渡る。

const promise = fetch('https://weather.tsukumijima.net/api/forecast/city/250010');

// ここから続き
promise
  .then(response => {
    console.log(response);
  });

// 結果: Response オブジェクトが引数に渡る.
Response{...}

値として返るわけでなく、引数として渡るため若干違和感があるけど、こういうものという理解で。

失敗バージョン

catch() を使用する。
catch()はエラーが引数に渡る。

// 適当なURLを設定
const promise = fetch('http://localhost');

promise
  // then を catch に書き換える. 引数も error に変更.
  .catch(error => {
    console.log(error);
  });

// 結果: エラーメッセージが確認できる.
TypeError: Failed to fetch

失敗と聞くと 404 Not Found500 Internal Server Error 等も含まれるように感じるがそうではない。この場合は then() が呼び出される。

今回のlocalhostのように存在しないドメインにfetch()をしてもWebサーバーにアクセスできず、Webサーバーからレスポンスが返ってこない。
そのような場合にcatch()ハンドラーが呼び出される。

404 の動きは後で確認する。

チェーンで処理を繋げる。

ここまでの処理を繋げる。

// 正規のURLを設定
const promise = fetch('https://weather.tsukumijima.net/api/forecast/city/250010');

promise
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

Promise の状態によって呼び出されるハンドラーが異なるため、競合は起こらない。
そのためハンドラーで処理をわけることができる。

次は then()の引数に渡った Response オブジェクトを確認していく。

Response オブジェクト

Response オブジェクトとは

サーバーのレスポンスが格納されたオブジェクト。

Response オブジェクトを確認

MDNにプロパティとメソッド一覧がある。
https://developer.mozilla.org/ja/docs/Web/API/Response

ok プロパティを確認してみる。

const promise = fetch('https://weather.tsukumijima.net/api/forecast/city/250010');

promise
  .then(response => {
    // ok を追加.
    console.log(response.ok);
  })
  .catch(error => {
    console.log(error);
  });
  
// 結果
true

ok プロパティはHTTPステータスコードが 200-299 の成功レスポンスで true となる。

Response オブジェクトで 404 に対応する

このokプロパティを使用すれば先程の 404 等を判別できる。

今回はドメインは正規のものを使用し、以下適当なパスに変更し 404 を確認する。
https://weather.tsukumijima.net/api/foreca/250010

const promise = fetch('https://weather.tsukumijima.net/api/foreca/250010');

promise
  .then(response => {
    console.log(response.ok);
  })
  .catch(error => {
    console.log(error);
  });
  
// 結果
false

catch()は呼び出されず、 then()が呼び出されている。
また、 200-299 以外なので false が返される。

ということで条件分岐を追加して処理を分けてみる。

ついでに Responseオブジェクトの json() メソッドも確認する。

const promise = fetch('https://weather.tsukumijima.net/api/forecast/city/250010');

promise
  .then(response => {
    if (response.ok) {
      // json を追加.
      console.log(response.json());
      return response.json();
    }
    // 200-299 以外の処理.
    throw new Error(`リクエストエラー ${response.status}`);
  })
  .catch(error => {
    console.log(error);
  });
  
// 結果: Promise オブジェクトが返される.
Promise

また Promise が返ってきた。
Response オブジェクトのMDNを確認すると json() によって Promise を返すと記載されている。

Promise の処理を進めるにはまた then() を使って処理を進める必要がある。

先に 404 の確認をしておく。

// ドメインはそのままで適当なパスに変更する.
const promise = fetch('https://weather.tsukumijima.net/api/fo/city/250010');

promise
  .then(response => {
    if (response.ok) {
      console.log(response.json());
      return response.json();
    }
    throw new Error(`リクエストエラー ${response.status}`);
  })
  .catch(error => {
    // message プロパティで投げられたエラーメッセージを標示する.
    console.log(error.message);
  });
  
// 結果
リクエストエラー 404

このようにして、404 等のエラーは条件分岐で処理する必要がある。

Promise オブジェクトを処理する

Response オブジェクトは json() メソッドによって Promise オブジェクトを返すことがわかった。
同様に then() で チェーンを繋ぐと、また引数に何かしらの値が渡る。

promise
  .then(response => {
    if (response.ok) {
      return response.json();
    }
    throw new Error(`リクエストエラー ${response.status}`);
  })
  // 引数を data とする.
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.log(error);
});

// 結果: json のデータが出力される.
{publicTime: '2023-01-20T20:00....'}

ようやく json のデータが取得できた。

表示してみる。

index.html
<div id="app"></div>
index.js
const promise = fetch('https://weather.tsukumijima.net/api/forecast/city/250010');

promise
  .then((response) => {
    if (response.ok) {
      return response.json();
    }
    throw new Error(`リクエストエラー ${response.status}`);
  })
  .then((json) => {
    const { forecasts } = json;
    const today = forecasts[0];
    const output = `
      <section>
        <img src="${today.image.url}" alt="${today.image.title}" width="${today.image.width}" height="${today.image.heght}"/>
        <div>
          <span>${today.temperature.max.celsius}° ${today.temperature.min.celsius}</span>
        </div>
        <ul>
          <li>0-6: ${today.chanceOfRain.T00_06}</li>
          <li>6-12: ${today.chanceOfRain.T06_12}</li>
          <li>12-18: ${today.chanceOfRain.T12_18}</li>
          <li>18-24: ${today.chanceOfRain.T18_24}</li>
        </ul>
      </section>
    `;
    const app = document.getElementById("app");
    app.innerHTML = output;
  })
  .catch((error) => {
    console.log(error);
  });

終わります。

追記

成功レスポンス以外では throw を使っていたが、複雑な処理ではPromise.reject()を使用したほうが良い。
本来のエラーなのかスローされたエラーなのか判別がつかないから。

具体的には下記のようになる。

promise
  .then(response => {
    if (response.ok) {
      return response.json();
    }
    // ここを promise.reject() に変更.
    promise.reject(`リクエストエラー ${response.status}`);
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.log(error);
});

// 結果: json のデータが出力される.
{publicTime: '2023-01-20T20:00....'}

https://azu.github.io/promises-book/#not-throw-use-reject

参考サイト

フェッチAPI: https://developer.mozilla.org/ja/docs/Web/API/Fetch_API
fetch(): https://developer.mozilla.org/ja/docs/Web/API/fetch
プロミス: https://developer.mozilla.org/ja/docs/Learn/JavaScript/Asynchronous/Promises
Request: https://developer.mozilla.org/ja/docs/Web/API/Request

Discussion