たけるのプログラミング

作ったものとか、気ままにアップします。

【JavaScript】非同期処理 Promise,thenについて色々試す【殴り書き】

前に以下のような記事を書いたことがありましたが、
【JavaScript】Promiseのthenメソッドの挙動について ちょっとした確認 - たけるのプログラミング

また非同期処理を実装する機会があったので、復習がてら色々なコードを試していけたらと思います。

そもそも非同期処理については、この記事を見るよりも良質な記事がたくさんあるので割愛しますが、

簡単に説明すると

JavaScriptはシングルスレッドでの実行になるので、例えばDBからデータを取ってくるような処理で多くの時間を要する場合、
その後に続く処理は、前の処理が終わるのを待たなくてはいけません。

そんな問題を解決するために利用されるのが非同期処理です。


fetchを使ってjsonデータを取得する

      const response = fetch("http://localhost/fetchtest")
      .then(function(response){
          console.log('データ取得しました!');
          return response.json();
      }).then(function(json){
          console.log(json);
      });

      console.log(response);
      console.log('(1)');
      console.log('(2)');
      console.log('(3)');

実行結果

まずどこからfetchをしているのかというとローカルに立ち上げたlaravelプロジェクトです。JavaScriptの話題とは関係ないですが、
laravelではモデルのインスタンスを要素に持つコレクションをreturnするだけでjson形式でフロントに返すことが出来ます。便利ですね。
またこのfetchは5秒かかるように、laravel側でsleep関数を使って処理を止めています。なのでJavaScriptを動かしているフロント側では
データを取得するのに5秒かかるように見える訳です。このように試している記事は自分が調べる限りまだ見てません。

fetchは非同期で行われる時間のかかる処理なのでまず

      console.log(response);
      console.log('(1)');
      console.log('(2)');
      console.log('(3)');

上記のコードの実行結果が先に表示されます。

console.log(response);

fetchメソッドはPromiseインスタンスを返します。
まず上記のコードでPromiseインスタンスの状態を見ることが出来ます。上記に載せた実行結果で
PromiseStateの値がpeddingとなっていました。このpendingは待機の状態のことで、言い換えるとlaravelからデータを取得している最中ということになります。
その後その処理が成功するとfulfilled
その後その処理が失敗するとrejectedとなります。


その後fetchメソッド成功するとPromiseStateがfulfilledとなります。
個人的な推測ですがfetchメソッドの内部にてresolveメソッドが実行されPromiseStateがfulfilledになっているのかなと思っています。

Promiseがfulfilledの状態になるとその後にthenメソッドを繋げて、非同期処理が成功した後に行いたい処理を定義することが出来ます。

thenメソッドの引数のコールバック関数の引数には、PromiseのPromiseResultの値が入っています。

またこのPromiseのPromiseResultはresolveメソッドの引数に指定した値が入ります。

console.log(new Promise(function(resolve){ resolve('JavaScript')}));

実行結果


なので1つ目のthenのコールバック関数の引数のresponseにはResponseインスタンスが入っていました。
つまり上記のことからfetch内部ではresolve(Responseインスタンス)のようになっていたと予想することが出来ます。

      const response = fetch("http://localhost/fetchtest")
      .then(function(response){
          console.log('データ取得しました!');
          return response.json();
      }).then(function(json){
          console.log(json);
      });

ResponseオブジェクトのjsonメソッドはPromiseインスタンスを返します。
ここで起こる疑問ですが、thenのコールバック関数にてPromiseインスタンスを返したら、thenメソッドはどのような値を返すのか。
結論からいうとthenはPromiseインスタンスを返しますが、コールバックのreturnによってそのPromiseインスタンスの状態は変わります。

ここから自分なりの解釈を含みます。
mdn web docsによると

Promise.prototype.then() - JavaScript | MDN

ハンドラ関数が
すでに履行されたプロミスを返した場合、 then によって返されるプロミスは、そのプロミスの値をその値として返します。

とありました履行=成功、値=PromiseResultのことかなと思います。
つまりreturn response.jsonで返されるPromiseインスタンスのPromiseResultの値が
thenメソッドで返されるPromiseインスタンスのPromiseResultの値に設定されるのではないかと思っています。

そうだとPromiseResultの値が次のthenのコールバック関数の引数に設定される事実と辻褄があいます。

thenのコールバック関数のreturnがpendding状態のPromiseインスタンスだった場合

new Promise(function(resolve){
        resolve(100);
    }).then(function(num){
        return new Promise(function(resolve){
            setTimeout(() => {
                resolve();
            }, 10000);
        });
    }).then(function(){
        console.log('これは実行される');
    });

実行結果で約10秒後に'これは実行される'と表示されました。
mdnドキュメントによると
Promise.prototype.then() - JavaScript | MDN

コールバック関数が

他の待機状態のプロミスオブジェクトを返した場合、 then によって返されたプロミスの解決/拒否は、ハンドラーによって返されたプロミスの解決/拒否結果に依存します。また、 then によって返されたプロミスの解決値は、ハンドラーによって返されたプロミスの解決値と同じになります。

特に以下の部分が大切ですね。そう考えるとjson()もこっちの例だったかもしれない。いやそうだ。

then によって返されたプロミスの解決/拒否は、ハンドラーによって返されたプロミスの解決/拒否結果に依存

そもそもreturnした後もPromiseのコールバック関数って実行されるんですね。

console.log(  new Promise(function(resolve){
        resolve(100);
    }).then(function(num){
        return new Promise(function(resolve){
            setTimeout(() => {
                console.log('returnした10秒後にresolveします');
                resolve(100);
            }, 10000);
        });
    }));

実行結果

なのでthenのコールバック関数がreturnした10秒後
thenで返したPromiseインスタンス上記の実行結果から
PromiseStateがfulfilledに変わって、PromiseResultが100に変わります。