Promiseの基本

Promiseとは


非同期処理を抽象化したオブジェクトとそれを操作する仕組みのことを指す。

Javascriptの概念ではなく、プログラミング言語におけるデザインの一種。


JSにおける非同期処理にはコールバック関数を利用するものもあるが、Promiseは非同期に対してオブジェクトとルールが仕様化され統一的な書き方をするようになっている。

var promise = getAsyncPromise("fileA.txt"); 
promise.then(function(result){
    // 取得成功の処理
}).catch(function(error){
    // 取得失敗時の処理
});

非同期処理を抽象化したpromiseオブジェクトを用意し、そのprimiseオブジェクトに対して.then と .catch を使って成功時の処理と失敗時の処理の関数を登録して使う。


コンストラク


promiseは、コンストラクタ関数であるPromiseからインスタンスとなるprimiseオブジェクトを作成して利用する。

var promise = new Promise(function (resolve, reject) {
    // 非同期の処理を書く
});



インスタンスメソッド


生成したpromiseオブジェクトにはpromise.then()というインスタンスメソッドがある。
promiseの処理がresolve(成功)あるいはreject(失敗)したときに呼ばれるコールバック関数を登録するために使う。

promise.then(onFulfilled, onRejected)

// 成功した時→ onFulfilled が呼ばれる
// 失敗した時→ onRejected が呼ばれる


promise.thenでは成功時と失敗時の処理を同時に登録することができるが、エラー処理だけを書きたい場合は primise.catch(onRejected)を使用する。


スタティックメソッド


Promiseというグローバルオブジェクトには静的なメソッドが存在する。
これらはPromiseを扱う上での補助的なメソッドが中心。
Promise.all()Promise.resolve()など



流れ


// 関数を定義
function asyncFunction() {
    // Promiseをnewしてpromiseオブジェクトを返す
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('Async Hello world');
        }, 16);
    });
}
// 関数がpromiseオブジェクトを返すので、.thenで成功時の処理を登録
asyncFunction().then(function (value) {
    console.log(value);    // => 'Async Hello world'
})
  // .catchで失敗時の処理を登録
  .catch(function (error) {
    console.error(error);
});

このpromiseオブジェクトはsetTimeoutで16ms後にresolveされるので、そのタイミングで.thenのコールバックが呼ばれ、'Async Hello world' と出力される。
失敗した場合にはcatchが呼ばれる。

先ほど述べたように、catchを使わずに記述することも可能。

asyncFunction().then(function (value) {
    // 成功時
    console.log(value);
}, function (error) {
   // 失敗時
    console.error(error);
});




Promiseの状態


new Promiseでインスタンス化したpromiseオブジェクトには3つの状態が存在する。

  • Fulfilled
    resolve(成功)した時。onFulfilledが呼ばれる

  • Rejected
    reject(失敗)した時。onRejectedが呼ばれる

  • Pending
    上のどちらでもない時、つまり初期状態。


ポイントとしては、一度PendingからFulfilledかRejectedになると、そのpromiseオブジェクトの状態はそれ以降変化することはない。
つまり.thenなどで登録した関数が呼ばれるのは1回限り。
また、FulfilledとRejectedどちらかの状態であることをsettked(不変の)と表現することもあるらしい。

promiseオブジェクトの状態が変化したときに一度だけ呼ばれる関数を登録するのが.thenなどのメソッドということになる。


Promiseを書いてみる


まずpromiseオブジェクトを作成するための関数を作る。

axiosを使って郵便番号から住所を取得するAPIをgetし、その結果によってresolveとrejectedに分岐させる。

      getZipAddressApi(zipCode) {
        return new Promise( function (resolve, reject) {
          axios.get('https://api.zipaddress.net/', {
            params: {
              zipcode: zipCode
            }
          })
            // axiosはリクエストが失敗しない限りFulfilledを返す
            .then(function (res) {
              // なのでJSON内のステータスコードで分岐
              if(res.data.code === 200){
                resolve(res.data.data.fullAddress);
              } else {
                reject(new Error(res.data.message));
              }
            });
        });
      },

コメントにもあるように、axios.getに対する.thenはURLが間違っていたなどの「リクエストそのものの失敗」が発生しない限りFulfilledになるので、.thenの中でAPIから帰ってきたJSON内のステータスコードで分岐させた。


f:id:naito-coding0322:20190111161946p:plainf:id:naito-coding0322:20190111161956p:plain

次に、今作成したpromiseオブジェクトを返すための関数を作成する。

      fetchAddress(){
        let zipCode = this.zipCode;
        // promiseオブジェクトを返す関数
        this.getZipAddressApi(zipCode)
          // 成功時の処理
          .then(value =>{
            this.address = value;
          })
          // 失敗時の処理
          .catch(error =>{
            this.address = '取得に失敗しました'
          })
      }



Vueコンポーネントのdataを含めた全体は以下の通り

<script>
import axios from 'axios';

  export default {
    name: 'zip-code',
    data(){
      return {
        zipCode: '',
        address: ''
      }
    },
    methods: {
      getZipAddressApi(zipCode) {
        return new Promise( function (resolve, reject) {
          axios.get('https://api.zipaddress.net/', {
            params: {
              zipcode: zipCode
            }
          })
            // axiosはリクエストが失敗しない限りFulfilledを返す
            .then(function (res) {
              // なのでJSON内のステータスコードで分岐
              if(res.data.code === 200){
                resolve(res.data.data.fullAddress);
              } else {
                reject(new Error(res.data.message));
              }
            });
        });
      },
      fetchAddress(){
        let zipCode = this.zipCode;
        this.getZipAddressApi(zipCode)
          // 成功時の処理
          .then(value =>{
            this.address = value;
          })
          // 失敗時の処理
          .catch(error =>{
            this.address = '取得に失敗しました'
          })
      }
    }
  };
</script>




Promiseの基本的な書き方はこんな感じ。
thenとかcatchとかがよく分かってなかったので、仕組み含めて理解できてよかった。

あとaxiosがPromiseベースだってこともよく分かった。 実は今まで思考停止的にaxios.get(URL).then(res => {})って書いてた。



参考リンク

azu.github.io