らばーいもっきんぐ

プログラミング等々についての覚え書き

【JavaScript】Promiseについて学んでみる

はじめに

JavaScriptのasync/awaitについて調べたら、その前にPromiseを理解する必要がありそうだったので、まずPromiseについて調べてみた。

Promiseとは

MDNでPromiseについて調べると、以下のように書かれている。

Promise は非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。

雑にまとめると以下のような感じ。

  • PromiseはES2015で導入された機能で、非同期処理と一緒に使われることが多い。

  • Promiseを使うと非同期処理の実行結果が成功した場合の処理と、失敗した場合の処理を簡潔に記述することができる。

Promiseオブジェクトを定義する

まずはPromiseの定義方法から。 Promiseは以下のように定義することができる。

new Promise(function(resolve, reject){ 
  //非同期の処理 
})

Promiseオブジェクトをインスタンス化する際には引数に関数を指定する(上記のfunctionの部分)。

この関数には最初から2つのパラメーターresolverejectが渡されている。 resolveとrejectは、どちらもそれ自体が関数で、その後に記述する非同期処理の成功と失敗を通知するために用いられる。

非同期の処理が成功した場合は、resolve関数に成功時に通知するメッセージや値などを渡す。

失敗した場合についてはreject関数に、失敗時に通知するメッセージや値などを渡す。

な...何を言っているかわからねーと思うので以下に例を示す。

function hoge(value) {
  return new Promise((resolve, reject) => {
    if(value) {
        resolve("ふるえるぞハート! 燃えつきるほどヒ――――――ト!!")
    } else {
        reject("貧弱! 貧弱ゥ!")
    }
  })
}

まずはPromiseオブジェクトについて確認するため、上記の例では肝心の非同期処理は一切実装されていない。

hoge関数はreturn new Promiseとあるように、最終的にはPromiseオブジェクトを返すようになっている。そしてPromiseの中ではhoge関数の引数valueがtrueかfalseかでresolveとrejectに成功時と、失敗時のメッセージを与えている。

今の状態でconsole.log(hoge(true))hoge関数の結果を表示してみると以下のようになる。

f:id:oaths3:20200302225327p:plain

hoge関数からはPromiseオブジェクトが返されていることが分かる。

また、PromiseStatusがresolvedとなっている。これはhoge関数から返されたPromiseオブジェクトがresolved状態であることを示している。

console.log(hoge(false))とすればhoge関数のvalueは偽になり、PromiseStatusはrejectedとなる。

resolvedやrejectedはPromiseオブジェクトの状態を示すもの。

Promiseの状態

Promiseには以下3つの状態というものがある。 以下はMDNから引用。

  • pending: 初期状態。成功も失敗もしていません。

  • fulfilled(resolved): 処理が成功して完了したことを意味します。

  • rejected: 処理が失敗したことを意味します。

Promiseの中で定義した処理が成功したか失敗したかにより、pending状態だったPromiseオブジェクトがresolvedかrejectedのどちらかの状態に変化する。

Promiseオブジェクトを利用する

Promiseオブジェクトの定義方法を説明したが、今のままではhoge関数はPromiseオブジェクトを返すだけなので、成功時や失敗時の値を取得することができない。 そこでPromiseオブジェクトを利用するためのメソッドがthenメソッドやcatchメソッドになる。

thenメソッドについて

thenメソッドを使うとPromiseオブジェクトの結果を受け取ることができる。

promise.then(成功時処理、失敗時処理)

thenメソッドの第一引数には成功時に実行したい処理を記述し、第2引数には失敗時に実行したい処理を記述する事ができる。

hoge(true).then(
  // 第一引数に成功時の処理を書く
  response => {
    console.log(response);
  },
 // 第二引数に失敗時の処理を書く
  error => {
    console.log(error);
  }
);
// => ふるえるぞハート! 燃えつきるほどヒ――――――ト!!

上記の例ではhoge関数にtrueを渡して実行しているので、hoge関数はresolved状態のPromiseオブジェクトを返す。

返ってきたPromiseオブジェクトに対しthenメソッドを呼び出すことで、その結果を受け取ることができる。thenメソッドの引数には2つのコールバック関数が定義されている。どちらも単純に受け取った結果をコンソールに表示するようになっている。

Promiseオブジェクトがresolved状態なら、thenメソッドの第一引数に指定したコールバック関数が実行される。Promiseオブジェクトの結果をresponseとして受け取り、それをコンソールに表示している。

同様に、hoge関数にfalseを渡して実行すれば、hoge関数はrejected状態のPromiseオブジェクトを返し、thenメソッドの第二引数で指定したコールバック関数が、Promiseの結果をerrorとして受け取り、それをコンソールに表示する。

ちなみにresponseやerrorは任意の名前を付けて構わない。

catchメソッドについて

先程はPromiseオブジェクトの結果をthenメソッドで受け取り、thenメソッドの中で成功時と失敗時の処理を記述していたが、thenメソッドの中には成功時の処理だけを記述し、失敗時の処理をthenメソッドの中から切り離すことができる。

そのためにはcatchメソッドを利用する。

hoge(false)
  .then(
    response => {
      console.log(response)
    }
  )
  .catch(
    error => {
      console.log(error)
    }
  );
// => 貧弱! 貧弱ゥ!

上記のようにthenメソッドの後にcatchメソッドを呼び出し、catchメソッドの中で失敗時の処理を記述する。

hoge関数にfalseを渡して実行しているので、hoge関数はrejected状態のPromiseオブジェクトを返す。この場合thenメソッドは無視されcatchメソッドだけが実行される。

catchメソッドで失敗時の処理を切り分けることで、可読性が向上したり、処理を一箇所にまとめることができる。

Promiseチェーンについて

thenメソッドは何度でも繋げることができる。以下はMDNで使われている例。

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);

上記の例ではthenが3回使われている。

doSomething関数はPromiseオブジェクトを返し、その結果が1つ目のthenに渡される。1つ目のthenの結果は2つ目のthenに渡され、2つ目のthenの結果は3つ目のthenに渡される。そして、いずれかの処理で失敗した場合には最後のcatchが呼ばれることになる。

ちなみにPromiseを使わないで上記のような処理を書くと以下のようになる。

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

これがいわゆるコールバック地獄というやつで非常に分かりづらい。

Promiseを使うと簡潔に書けてとっても嬉しいことが分かる。

まとめ

Promiseについてなんとなく理解することができた。例として記述したコードに非同期処理の部分が無いためイマイチメリットが伝わらないかもしれない。axiosなど使ってみるとナルホドとなることがあるかもしれません。