簡単学習Promise対象


Promiseは非同期的にプログラムされた解決策であり、従来の解決策であるフィードバックとイベントよりも合理的で強力である.最近のプロジェクトはこれを使います.阮一峰先生の「ES 6標準入門」という本を参考にして簡単に勉強してみます.
1 Promiseの意味
Promiseとは、簡単に言えば、ある未来が終わるイベント(通常は非同期操作)が保存されているコンテナのことです.文法的には、Promiseはオブジェクトであり、非同期操作のメッセージを取得することができます.Promiseは統一APIを提供しています.様々な非同期操作は同じ方法で処理できます.
Promiseオブジェクトには以下の2つの特徴があります.
  • オブジェクトの状態は外部の影響を受けない.
  • いったん状態が変わると、もう変わりません.
  • 2基本的な使い方
    ES 6はPromiseオブジェクトがPromiseのインスタンスを生成するためのコンストラクタであると規定しています.例を挙げる
    var promise = new Promise(function (resolve, reject) {
      // some code
      if (/*       */) {
         resolve(value);
      } else {
        //       
        reject(error);
      }
    });
    Promise構成関数は、 をパラメータとして受信し、 の2つのパラメータはそれぞれresolveおよびrejectである.彼らは2つの関数で、Javascriptエンジンによって提供され、自分で配置しなくてもいいです.resolve関数の役割は、Promiseオブジェクトの状態を「未完成」から「成功」に変化させ、非同期動作が成功したときに呼び出し、非同期動作の結果をパラメータとして過去に伝えることである.reject関数の役割は、Promiseオブジェクトの状態を「未完成」から「失敗」に変えることです.
    私たちがPromiseのインスタンスを生成した後.then方法で、Resolved状態およびRejected状態のコールバック関数をそれぞれ指定することができる.
    promise.then(function (value) {
      // success
      console.log(value);
    }, function (error) {
      // failed
      console.log(error);
    });
    then方法は、パラメータとして2つのコールバック関数を受け入れることができる.第1のコールバック関数はPromiseオブジェクトの状態がResolvedになったときに呼び出します.第2のコールバック関数はPromiseオブジェクトの状態がRejectedになったときに呼び出します.第二のパラメータはオプションであり、必ずしも提供する必要はない.これらの2つの関数は、パラメータとしてPromiseオブジェクトからの値を受信する.
    ちょっと例をあげます.
    let promise = new Promise(function(resolve,reject){
      console.log('Promise');
      let value = 'value';
      resolve(value);
    });
    
    promise.then(function(value){
      console.log(value);
    });
    console.log('Hi');
    
    // Promise
    // Hi
    // value
    上記のコードでは、Promiseは新規作成後すぐ実行されますので、まず出力されるのはPromiseです.その後、then方法で指定されたコールバック関数は、現在のスクリプトの全同期タスクを実行した後に実行されるので、Resolvedは最後に出力する.
    3 Promise.prototype.then()
    Promiseの例は、then方法、すなわちthen方法がプロトタイプオブジェクトPromise.prototypeに定義されている.その役割はPromiseの例に状態を変える際のコールバック関数を追加することである.前に述べたように、then方法の第1のパラメータはResolved状態のコールバック関数であり、第2のパラメータ(オプション)はRejected状態のコールバック関数である.then方法は、新しいPromise である( は元のPromiseの例ではない).したがって、チェーン式の書き方、すなわちthen方法の後に別のthen方法を呼び出すことができる.
    getJSON("/posts.json").then(function(json) {
      return json.post;
    }).then(function(post) {
        // ...
    });
    上記のコードはthen方法を用いて、二つのコールバック関数を順次指定した.最初のコールバック関数が完了すると、パラメータとして結果を返します.
    その後、チェーンを採用したthenは、順番に呼び出されたコールバック関数のセットを指定することができる.このとき、前回のコールバック関数は、元のPromiseオブジェクトに戻る可能性があります.すなわち、非同期動作があります.その後、一つのコールバック関数は、Promiseオブジェクトの状態が変化するのを待って呼び出します.
    4 Promise.prototype.ctch()Promise.prototype.catch方法は、エラーが発生した場合のコールバック関数を指定する.then(null, rejection)の別名である.
    getJSON('/posts.json').then(function(posts) {
      // ...
    }).catch(function(error) {
      //    getJSON                  
      console.log('    !', error);
    });
    上記のコードにおいて、getJSON方法は、オブジェクトの状態がResolvedになると、then方法で指定されたコールバック関数を呼び出します.非同期動作がエラーを出すと、状態はRejectedになり、catch方法で指定されたコールバック関数を呼び出してこのエラーを処理します.また、then方法で指定されたコールバック関数は、実行中にエラーが発生した場合、catch方法によって捕捉される.
    p.then((val) => console.log('fulfilled:', val))
      .catch((err) => console.log('rejected', err));
    
      //    
      p.then((val) => console.log('fulfilled:', val))
      .then(null, (err) => console.log("rejected:", err));
    次は一例です.
    const promise = new Promise(function(resolve, reject) {
      throw new Error('test');
    });
    promise.catch(function(error) {
      console.log(error);
    });
    // Error: test
    上記のコードでは、Promiseがエラーを出すとcatch方法で指定されたコールバック関数によって捕獲されます.上の書き方と下の2つの書き方は等価です.
    //    
    const promise = new Promise(function(resolve, reject) {
      try {
        throw new Error('test');
      } catch(e) {
        reject(e);
      }
    });
    promise.catch(function(error) {
      console.log(error);
    });
    
    //    
    const promise = new Promise(function(resolve, reject) {
      reject(new Error('test'));
    });
    promise.catch(function(error) {
      console.log(error);
    });
    上記から分かるように、reject方法の役割はミスを投げかけることに等しい.Promise状態がすでにResolovedになっている場合、エラーをスローするのは無効です.
    const promise = new Promise(function(resolve, reject) {
      resolve('ok');
      // Promise          
      throw new Error('test');
    });
    promise
      .then(function(value) { console.log(value) })
      .catch(function(error) { console.log(error) });
    // ok
    一般的には、thenの状態のコールバック関数(Rejectの第2のパラメータ)は、thenの方法で定義されずに、catchの方法が使用される.
    // bad
    promise
      .then(function(data) {
      // success
      }, function(err) {
      // error
    });
    
    // good
    promise
      .then(function(data) { //cb
      // success
      })
      .catch(function(err) {
      // error
      });
    上記のコードの中で、第二の書き方は第一の書き方よりも優れています.理由は、第二の書き方が前のthen方法の実行中のエラーをキャプチャし、同期の書き方(try/catch)にもより近いからです.したがって、catch方法の第2のパラメータを使用せずに、常にthen方法を使用することを提案する.
    5 Promise.all()Promise.all方法は、複数のPromiseオブジェクトのインスタンスを新たな例に包装することである.
    var p = Promise.all([p1, p2, p3]);
    上記のコードでは、Promise.all()方法は、パラメータとして1つの配列を受け入れ、p 1、p 2、p 3はPromiseオブジェクトの一例である.もしそうでないならば、先に以下に述べたPromise.resolve方法を呼び出して、パラメータをPromiseの例に変換して、更に処理する(Promise.all方法のパラメータは必ずしも配列ではないが、Iteratorインターフェースを備えていなければならず、各返却メンバーはPromiseの例である).
    pの状態はp 1,p 2,p 3で決まります.
  • はp 1、p 2、p 3の状態がすべてFulFilledとなり、pの状態がFulFilledとなり、このときp 1、p 2、p 3の戻り値が1つの配列をなしてpのコールバック関数に伝達される.
  • p 1,p 2,p 3のいずれかがRejectedによって、pの状態がそのままRejectdに変わり、このとき最初のRejectedのインスタンスの戻り値がpのコールバック関数に伝達される.
  • 以下は具体例です.
    var promises = [2,3,4,5].map(function(id){
          console.log(id)
        });
        
        Promise.all(promises).then(function(res){
          console.log(res);
          resolve
        }).catch(function(error){
          console.log(error);
        });
    
    //       promise        ,              
    // 2 3 4 5 [undefined,undefined,undefined,undefined]
    上のコードでは、Promiseは6つのPromiseの例を含む配列であり、この6つの例の状態だけがフルフィルドになり、またはそのうちの1つがrejectiedになり、Promise.allアプローチの後のコールバック関数が呼び出される.
    6 Promise.race()Promise.race方法は、複数のPromiseのインスタンスを新たなPromiseのインスタンスにパッケージ化することでもある.
    var p = Promise.race([p1, p2, p])
    上のコードの中で、p 1、p 2、p 3の が率先して状態を変える限り、pの状態は変化します.その先に変わったPromiseの例の戻り値がpのコールバック関数に伝達されます.Promise.race方法のパラメータは、Promise.all方法と同様に、Promise例でない場合は、先に以下に述べるPromise.resolve方法を呼び出して、パラメータをPromiseの例に変えて、さらに処理する.
    次の例では、指定された時間内に結果が得られなかったら、Promiseの状態をRejectに変更します.そうでないとResolivedになります.
    const p = Promise.race([
      fetch('/resource-that-may-take-a-while'),
      new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('request timeout')), 5000)
      })
    ]);
    
    p
    .then(console.log)
    .catch(console.error);
    上記のコードでは、5秒以内にfetch方法で結果が戻らないと、変数pの状態がrejectに変化し、catch方法で指定されたコールバック関数をトリガする.
    7 Promise.resove()
    既存のオブジェクトをPromiseオブジェクトに変換する必要がある場合があります.Promise.resolve方法がこの役割を果たします.
    const jsPromise = Promise.resolve($.ajax('/whatever.json'));
    上のコードはjQueryが生成したdeferredオブジェクトを新しいPromiseオブジェクトに変えます.Promise.resolveは、次のような書き方に等しい.
    Promise.resolve('foo')
    //    
    new Promise(resolve => resolve('foo'))
    Promise.resove方法のパラメータは四つの場合に分けられます.
    7.1パラメータはPromiseの例です.
    パラメータがPromiseの例である場合、Promise.resolveは任意の変更を行わずに、そのままこの例に戻ります.
    7.2パラメータは一つのthenableオブジェクトです.
    thenableオブジェクトとは、thenメソッドを有するオブジェクト、例えば、以下のオブジェクトのことです.
    let thenable = {
      then: function(resolve, reject) {
        resolve(42);
      }
    };
    Promise.resolve方法は、このオブジェクトをPromiseオブジェクトに変換し、thenableオブジェクトのthen方法を実行します.
    let thenable = {
      then: function(resolve, reject) {
        resolve(42);
      }
    };
    
    let p1 = Promise.resolve(thenable);
    p1.then(function(value) {
      console.log(value);  // 42
    });
    上のコードでは、thenableオブジェクトのthenメソッドが実行されると、オブジェクトp 1の状態がresolovedとなり、最後のthenメソッドで指定されたコールバック関数が直ちに実行される.出力42
    7.3パラメータは、thenメソッドを有するオブジェクトではなく、またはオブジェクトではない.
    パラメータが元の値であるか、またはthen方法を持たないオブジェクトである場合、Promise.resolved方法は新しいPromiseオブジェクトを返し、状態はResovedである.
    const p = Promise.resolve('Hello');
    
    p.then(function (s){
      console.log(s)
    });
    // Hello
    上のコードは新しいPromiseオブジェクトのインスタンスpを生成します.文字列ハローは非同期操作であるため(判定方法は文字列オブジェクトがthenメソッドを持っていない)、Promiseのインスタンスに戻った状態は生成からresovedであるため、コールバック関数は直ちに実行される.Promise.resolve方法のパラメータは、同時にコールバック関数に伝えられます.
    7.4どのパラメータも持たないPromise.resolved方法は、起動時にパラメータを持たずにResoved状態のPromiseオブジェクトに直接戻すことを可能にする.
    ですから、もしPromiseオブジェクトがほしいなら、便利な方法は直接Promise.resolveメソッドを呼び出すことです.
    const p = Promise.resolve();
    
    p.then(function () {
      // ...
    });
    上のコードのpがPromiseオブジェクトです.注意したいのは、すぐにレレスのPromiseオブジェクトが本物のサイクル『イベントサイクル』(event loop)を終了した時で、次の『イベントサイクル』の開始時ではないことです.
    setTimeout(function () {
      console.log('three');
    }, 0);
    
    Promise.resolve().then(function () {
      console.log('two');
    });
    
    console.log('one');
    
    // one
    // two
    // three
    上記のコードは、setTimeout(fn, 0)が次の「イベントサイクル」の開始時に実行し、Promise.resolve()が本ラウンドの「イベントサイクル」の終了時に実行し、console.log('one')が直ちに実行されるため、最初に出力される.
    8 Promise.reject()Promise.reject(resson)方法は新しいPromiseの例を返します.状態はRejectiedです.
    const p = Promise.reject('   ');
    //    
    const p = new Promise((resolve, reject) => reject('   '))
    
    p.then(null, function (s) {
      console.log(s)
    });
    //    
    上のコードはPromiseオブジェクトのインスタンスpを生成し、状態はRejectedであり、コールバック関数は直ちに実行されます.
    締め括りをつける
    以上、Promise学習の内容についてですが、間違ったところがあれば、下記のレビューで意見を発表してください.もちろん、進級についてのPromiseの文章を置いて、皆で勉強してもいいです.