2019-06-09に投稿

fetchがよくわからなかったのでオレオレfetchを作って雰囲気を掴む

前回、こんな記事を書きました。
上の記事のそもそもの発端は、fetchを使おうとしたけども、よくわからなかったからというものでした。
根本的には、fetchがわからないというよりはPromiseがよくわかってなかったという方が近いので、前回Promiseの雰囲気を掴んだ時点で7割方解決しているのだけれど、そもそもの発端を解決させようと思います。

そもそもの発端

超絶ざっくり書くと、fetchというのは下記のようになるかと。

fetch(url)
.then((response)=>{
    return response.json();
})
.then(()=>{
    //json使って何か処理
})
.catch((error)=>{
    //エラー処理
})

まあ、こんな感じのものを見て、非同期をよくわかっていなかった僕はちんぷんかんぷんになって、どうにか、fetchというのは非同期で動くものらしい、というところまではわかった。
なのでどうにかして非同期を勉強しなければいけない。
そこでググったら出てくるわけです。

const hoge = ()=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log('wait 3 sec!')
            resolve('wait 3 sec!')
        },3000)
    })
}

……。さっきまでPromiseとかresolveとかrejectとか出てこなかったじゃん。
ってなって挫折していた。
今思えば至極単純で、Promiseとかresolveとかrejectとかはfetchの中で勝手にやっていることなので、fetchを使う分には登場しない。
もちろん、理解する必要はあって、そのために今回もオレオレfetchを作ることで雰囲気を掴もうと思う。

注意

あくまで雰囲気を掴むだけです。実際の実装は微塵も知りません。
いろんなドキュメントを読んだ上での筆者の解釈(を筆者の実装レベルでできるとこだけ再現したもの)です。とんちんかんかもしれません。
妥協しまくりです。
あまりにもとんちんかんなこと言ってたら教えて下さい。
信用しないでください。
レベル不足でクソコードです。

まず呼び出すコードから

こんな感じのjsonを作ります。
json-serverとかで適当にapiを作ります。

{
    "wizards":[
        {
            "id":1,
            "name":"Arietta",
            "feature":"monster"
        },
        {
            "id":2,
            "name":"Relum",
            "feature":"fool"
        },
        {
            "id":3,
            "name":"Elise",
            "feature":"poor"
        }
    ]
}

で、呼び出し側

<html lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript" src="promise.js"></script>
        <script type="text/javascript" src="fetch.js"></script>
        <script type="text/javascript" src="index.js"></script>
        <style>
            table{
                border:solid 1px;
            }
            th{
                border:solid 1px;
            }
            td{
                border:solid 1px;
            }

        </style>
    </head>
    <body>
        hello async
        <button onclick="getData()">
            fetch
        </button>
        <table >
            <thead>
                <th>id</th>
                <th>name</th>
                <th>feature</th>
            </thead>
            <tbody id="tbody">

            </tbody>
        </table>
    </body>
</html>
const getData = () => {
    const url = 'https://localhost:3000/wizards'  //json-serverとかでよしなに
    fetch(url)
    //oreoreFetch(url) //これに置き換えても動くようにするのが目標
    .then((response)=>{
        return response.json();
    })
    .then((json)=>{
        const tbody = document.getElementById('tbody');
        json.forEach((wiz)=>{
            let tr = `  <tr>
                            <td>${wiz.id}</td>
                            <td>${wiz.name}</td>
                            <td>${wiz.feature}</td>
                        </tr>
            `;
            tbody.insertAdjacentHTML("beforeend",tr);
        });
    })
}

WS000001.JPG
まー、動くよね

で、fetchのコード

const oreoreFetch = (url)=>{

    return new Promise((resolve,reject)=>{
        httpRequest(resolve,reject,url)
    });
    //最終的に前回のオレオレPromiseで動いてほしい
    //けど、Promiseチェーンが実装できなかったんだよな…
    //return new oreorePromise((resolve,reject)=>{  
    //    httpRequest(resolve,reject,url)
    //});
}

const httpRequest = (resolve,reject,url) =>{

    const xhr = new XMLHttpRequest();

    xhr.onreadystatechange = () => {
        switch ( xhr.readyState ) {
            //めんどいので4以外省略
            case 0:
                break;
            case 1: // データ送信中.
                break;
            case 2: // 応答待ち.
                break;
            case 3: // データ受信中.
                break;
            case 4: // データ受信完了.
                if( xhr.status == 200 || xhr.status == 304 ) {
                    const data = new oreoreResponse(xhr.responseText);
                    resolve(data);
                } else {
                    reject(xhr.statusText);
                }
                break;
        }
    }
    xhr.open( 'GET', url, true );
    xhr.send(null);
}

class oreoreResponse{
    constructor(data){
        this.data = data;
    }

    json(){
        return JSON.parse(this.data);
    }
}

fetchができればいいのでResponseクラスは適当です。
(実際はコンストラクタに引数はないし、jsonメソッドはPromiseを返す)
Promiseの勉強で出てきたresolveとかrejectは(たぶん)fetchの中にあって、よしなにやってくれるので、fetchを使う分には意識しなくていいよ、ってのがわかる。

ということで動かす。

const getData = () => {
    const url = 'https://localhost:3000/wizards'
    //fetch(url)
    oreoreFetch(url) //これに置き換えても動くようにするのが目標
    .then((response)=>{
        return response.json();
    })
    .then((json)=>{
        const tbody = document.getElementById('tbody');
        json.forEach((wiz)=>{
            let tr = `  <tr>
                            <td>${wiz.id}</td>
                            <td>${wiz.name}</td>
                            <td>${wiz.feature}</td>
                        </tr>
            `;
            tbody.insertAdjacentHTML("beforeend",tr);
        });
    })
}

WS000000.JPG
まー、それっぽく動く。

まとめ

みんな、async/await使おうぜ!


hammhiko

恥を晒して生きていきます。

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント