Promiseオブジェクト.分からないことから考える編 / JavaScript/Vue.js

非同期処理を実行する際に利用する Promiseオブジェクト.
何となく分かるが、実際にコードを書いてみようとすると困ってしまう。という状況有りませんでしょうか。
何かが分かっていないが、何が分かっていないのか….という状況でした。

疑問として湧いてきたことをベースに、Promiseオブジェクトについてまとめていきたいと思います。

Q1:Promiseオブジェクトとは?

Promiseオブジェクトを単純に表現すると、状態を持つオブジェクトです。
3つの状態があります。
保留中(pending)
・成功(fulfilled)
・失敗(reject)

Promiseオブジェクトは常に3つのどれかの状態を持ちます。
これらの状態を持つという特徴があるからこそ、非同期処理を作ることが出来ます。

Q2:Promiseの状態はいつ変わる?

成功するか、失敗するかで、状態が変わります。
そのまんま過ぎますかね。では、次へ。

Q3:Promiseの成功 / 失敗はどう決まる?

非常に単純化すると、resolveが実行されたら成功・rejectが実行されたら失敗です。

Promiseオブジェクトの中身をconsoleで確認していきます。

conosole 結果 / 保留中(pending)
const promise = new Promise( (resolve, reject ) => {
      })
console.log(promise);
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined

Promiseオブジェクトは、[PromiseState] , [PromiseResult] の2つを持ちます。

上記では、PromiseStateはpendingとなっています。状態が何も変わっていない初期状態です。

conosole 結果 / 成功(fulfilled)

次に、Promiseの中でresolveを実行してみます。

const promise = new Promise( (resolve, reject ) => {
        resolve();
      })
console.log(promise);
Promise {<fulfilled>: undefined}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined

resolve()が実行されたPromiseは成功(fulfilled)の状態に変化しています。

conosole 結果 / ・失敗(reject)

次は予想がつくかと思いますが、
Promiseの中でrejectを実行してみます。

const promise = new Promise( (resolve, reject ) => {
        reject();
      })
console.log(promise);
Promise {<rejected>: undefined}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: undefined

成功のときと同様に
reject()が実行されたPromiseは失敗(reject)の状態に変化しています。

Q4:あれ? resolve, reject って、何?

ここで、ふと思ったのはresolve, reject って何だろうということでした….

結論から言うと、Promiseオブジェクトの
・第一引数:resolve
・第二引数:reject
※両方とも関数です。

名称は何でもOKで、第一引数に指定した関数が実行されれば成功、第二引数に指定した関数が実行されれば失敗  ※reject(第二引数)は省略が可

consoleで確認します。

const promise = new Promise( (daiichiHikisu, reject ) => {
  daiichiHikisu();
})
console.log(promise);
Promise {<fulfilled>: undefined}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined

resolve()を実行したときと同じく、成功(fulfilled)となっています。

つまり、名称は何でもOKで、第一引数に指定した関数が実行されれば成功となるのです。

Q5:PromiseResultは何?

Q4まで、PromiseオブジェクトのPromiseStateについて調べてきました。その間、PromiseResultは全てundefinedでした。

PromiseResultは、resolve OR rejectで返す値です。

const promise = new Promise( (resolve, reject ) => {
  resolve("success");
})
console.log(promise);
Promise {<fulfilled>: 'success'}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "success"

resolve()で返した値が、PromiseResultに含まれます。

Q6:で、非同期処理とPromiseオブジェクトの関係とは?

Promiseオブジェクトは、PromiseStateが変わることで状態が変化することはよく分かりました。結局、Promiseオブジェクトが非同期処理をどの様に実現するのでしょうか。

非同期処理を実現するために、Promiseオブジェクトの処理に大事なメソッドがあります。それは、then()と catch()です。

then(): Promiseの結果、fulfilledの場合に実行
catch(): Promiseの結果、rejectの場合に実行

実際に、consoleで確認します。

const promise = new Promise( (resolve, reject ) => {
  resolve();
})
  .then(() => {
    console.log("resolveしたよ");
  })
  .catch(() => {
    console.log("rejectしたよ");
  });

conosole 結果 / 成功(fulfilled)

resolveしたよ

逆に、rejectの場合

const promise = new Promise( (resolve, reject ) => {
  reject();
})
  .then(() => {
    console.log("resolveしたよ");
  })
  .catch(() => {
    console.log("rejectしたよ");
  });

conosole 結果 / ・失敗(reject)

rejectしたよ

となります。
下記の通りにthen, catchがそれぞれ実行されます。

then(): Promiseの結果、fulfilledの場合に実行
catch(): Promiseの結果、rejectの場合に実行

Q7:ででで、結局、非同期処理とPromiseオブジェクトの関係とは?

結局のところ、これで非同期処理をどうやって実現するのでしょうか。

・Promiseオブジェクトは状態を持つ
・状態に合わせて処理を変更できる

この2つの特性を利用して、非同期処理を実現することが出来ます。

setTimeout関数を利用して、1000ms後にresolve(“success”)を実行する関数を作ります。returnでPromiseオブジェクトを返します。

promiseAfter1000ms() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("success")
    }, 1000)
  })
}

そして、この関数の実行に対してthen(), catch()で状態に合わせて処理を実行します。

this.getValueAfter1000ms()
      .then(() => { console.log("good") })
      .catch(() => { console.log("error") })
conosole 結果 / 成功(fulfilled)

Promiseオブジェクトが成功(fulfilled)の際に実行されるthen()内のconsoleが、1000ms後に処理されます。

good
conosole 結果 / ・失敗(reject)

次に、reject処理が実行されるPromiseへ変更すると

promiseAfter1000ms() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("misstake")
    }, 1000)
  })
}

catch()内のconsoleが、1000ms後に処理されます。

error

Q8:より実践的に、Promiseオブジェクトで非同期処理を実行するには?

Q7にて、1000ms後に、Promiseの状態に応じて実行する非同期処理を記述しました。しかし、分かったような分からないような、使いみちがイメージしづらいかもしれません。

より実践的な処理にてイメージを掴みましょう。

サーバーにPostリクエストした後に、そのレスポンスデータが返ってきた後に実行する非同期処理を考えてみます。

サーバーにPostする処理をaxiosを使って書いていきます。
axiosとは、axiosの設定については触れません。
既知ではない方は、サーバーにリクエストしている。くらいに思っておいて下さい。

Promiseオブジェクトの中に axios処理を記述しています。

getServerData() {
  return new Promise( (resolve, reject) => {
    this.$axios.$post('/api/ques', { name: "test",}) // axios処理
     .then(res => resolve( res ))
     .catch(res => reject( res ))
  })
},

補足

※Postリクエストにて、nameプロパティに文字列testを送信
※axios処理自体も非同期処理になっている
※axios処理が成功した場合(fulfilled)、レスポンスでresが返ってくる

上記のaxiosによりPostする処理の結果を受けて、非同期処理を実行します。

axiosによるPostリクエストが成功した場合、thenが実行されます。失敗した場合にはcatchが実行されます。

this.getServerData()
      .then((res) => { console.log(res) })
      .catch((res) => { console.log(res) })

■参考にさせて頂いた情報

  1. https://www.tohoho-web.com/ex/promise.html
  2. https://qiita.com/cheez921/items/41b744e4e002b966391a
  3. https://hidekazu-blog.com/javascript-promise

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です