javascript初学者が一度はつまづくであろう、非同期処理。
promiseやらfetchやらを使う際に思ったように動かないことをいったい何度繰り返しているか・・・。
例に漏れず私も初学者であるので、日々頭を悩ませているわけですが、fetchおよびpromiseまわりで気づきがあったので同じように詰まった人向けの記録として書き残しておきます。
基礎的な内容でありながら、初学者ゆえ間違った理解をしている部分もあるかもしれませんので、なにかありましたらぜひご指摘ください。
promiseとは
そもそもpromiseとは何なのかを振り返っておきます。
- promiseは非同期処理の結果を成功(resolve)か失敗(reject)で返すオブジェクト
- 処理状態をfulfiled(正常終了)、pending(処理中)、rejected(拒否)というstatusで示す
- 通常はnew Promise()としてインスタンスを使って生成する
promiseの基本的な処理
例えばpromiseインスタンスに関しては以下の様なコードを良くみるかと思います。
const promise = new Promise( (resolve)=>{
console.log("最初");
resolve();
} );
promise.then( ()=>{ console.log("最後"); } );
変数promiseに格納したpromiseインスタンスをresolve()した上で、変数promiseに対してthen()の処理を行う様に指示しています。
これをresolve()を書かないで実行すると、thenメソッドの中の処理は動作しません。
なぜならthen()はresolve状態のpromiseオブジェクトでないと処理が発火しないからです。
fetch()の基本的な処理
続いてfetch()のよくある形のコードを見てみます。
url = "https://pokeapi.co/api/v2/pokemon/pikachu/"
const data = fetch( url )
.then( (response) => { return( response.json(); ) } )
.then( (data) => { console.log(data); } );
※ エラーの処理は省略しています
fetchメソッドはAPIのURLを引数にとって、promiseオブジェクトを返します。
返されたpromiseオブジェクトに対してthenメソッドを書くことでjson形式のデータを取得することができるわけですね。
ここで先ほどpromiseオブジェクトに対してthenメソッドの処理をすることについて述べた内容を思い出します。
「thenメソッドはresolve状態のpromiseオブジェクトに対してのみ動作する」のでした。
これを踏まえて考えると「fetchメソッドから返ってくるpromiseオブジェクトは、resolve状態なのではないか」と考えられます。
確認してみる
実際に単純なコードを書いて確認してみます。
url = "https://pokeapi.co/api/v2/pokemon/pikachu/"
const test = fetch( url ); //変数testにpromiseオブジェクトを格納
console.log( test );
setTimeout( () => { console.log( test ); }, 2000 );
実行結果
Promise {<pending>}
Promise {<fulfilled>: Response}
fetchメソッドを格納した変数testをそのままコンソールに出力したときはpending状態のpromiseオブジェクトを返している一方、setTimeoutで一旦タスクキューへの登録を遅らせてからconsole.logを使ってpromiseオブジェクトを呼び出すとfulfiled状態となっていることがわかります。
つまりfetchはpromiseを返す際に、サーバーからのレスポンスの取得が完了するとfulfiled状態となるっぽいことがわかります。
これを理解した上でpromiseを扱わないと、なぜかundefinedを返される、という罠にハマりつづけて悲しい結末を迎える様です。
fetchの中でresolve()をしなくて良い理由
上で述べた様にfetchは通信が成功しレスポンスの取得が完了するとfulfiledのpromiseオブジェクトを返す、という挙動をする様です。
なので、fetchの後にresolveの処理をしなくてもそのまま続けてthenチェーンを書くことができる、というわけなのですね。
fetchを触り始めの頃は、「fetchってpromiseオブジェクトを返しているはずだし、なんでthenメソッドをいきなりチェーンで書くことができるねん、、」と混乱しましたが、こういうことだったみたいです。