ES 6のpromise原理

11622 ワード

promiseの役割
Promiseは非同期プログラミングのソリューションであり、従来のソリューションであるコールバック関数よりも強力であり、階層的なコールバックを回避します.それはコミュニティが最も早く提出して実現して、ES 6はそれを言語の標準に書いて、用法を統一して、今原生はPromiseの対象を提供します!
Promise使用:
// ... some code
const promise = new Promise(function(resolve, reject) {
  if (/*        */){
    resolve(value);
  } else {
    reject(error);
  }
});
  • では、promiseはnewオペレータを使用してインスタンスを生成する必要があるため、Promiseは構造関数であることがわかります.
  • このpromiseを初期化するには、パラメータとして1つの関数を入力する必要があります.この関数の2つのパラメータは、resolveとrejectです.
  • resolveとrejectは2つの関数で、JavaScriptエンジンによって提供され、自分で配置する必要はありません.
  • resolve関数の役割:Promiseオブジェクトの状態をpendingからfulfilledに変更します.
  • reject関数の役割:Promiseオブジェクトの状態をpendingからrejectに変更します.

  • promiseの原理を見てみましょう
    Promiseはステータスツールで、pending、fulfilled、rejectの3つのステータスがあります.そのため、まず3つのステータスを定義します.
    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise() {
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value or error once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers attached by calling .then or .done
      var handlers = [];
    }
    

    Promiseは非同期操作を実行すると状態が遷移するので,コードはこのようになる.
    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise() {
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function fulfill(result) {
        state = FULFILLED; //      
        value = result;
      }
    
      function reject(error) {
        state = REJECTED; //      
        value = error;
      }
    }
    

    上のfulfillはlow levelを比較する遷移状態法ですが、higher-levelの遷移状態法があります:resolve
    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise() {
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function fulfill(result) {
        state = FULFILLED;
        value = result;
      }
    
      function reject(error) {
        state = REJECTED;
        value = error;
      }
    
      function resolve(result) {
        try {
          var then = getThen(result);
          if (then) {
            doResolve(then.bind(result), resolve, reject)
            return
          }
          fulfill(result);
        } catch (e) {
          reject(e);
        }
      }
    }
    

    resolveがメソッドgetthen()を先に呼び出しているのが見えますが、このメソッドはresultがpromiseであるかどうかを判断し、もしそうであれば、このpromiseのthenメソッド(doResolve()メソッドを使用)を呼び出し、そうでなければfulfill()メソッドを呼び出します.
    /**
     * Check if a value is a Promise and, if it is,
     * return the `then` method of that promise.
     *
     * @param {Promise|Any} value
     * @return {Function|Null}
     */
    function getThen(value) {
      var t = typeof value;
      if (value && (t === 'object' || t === 'function')) {
        var then = value.then;
        if (typeof then === 'function') {
          return then;
        }
      }
      return null;
    }
    
    /**
     * Take a potentially misbehaving resolver function and make sure
     * onFulfilled and onRejected are only called once.
     *
     * Makes no guarantees about asynchrony.
     *
     * @param {Function} fn A resolver function that may not be trusted
     * @param {Function} onFulfilled
     * @param {Function} onRejected
     */
    function doResolve(fn, onFulfilled, onRejected) {
      var done = false;
      try {
        fn(function (value) {
          if (done) return
          done = true
          onFulfilled(value)
        }, function (reason) {
          if (done) return
          done = true
          onRejected(reason)
        })
      } catch (ex) {
        if (done) return
        done = true
        onRejected(ex)
      }
    }
    

    ここでは、どのような状況でreject:catchからのエラーがrejectになるかを見ることができます.これまで、内部ステータスマシンを完了しましたが、resolvingというpromiseの方法を紹介します.
    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise(fn) {
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function fulfill(result) {
        state = FULFILLED;
        value = result;
      }
    
      function reject(error) {
        state = REJECTED;
        value = error;
      }
    
      function resolve(result) {
        try {
          var then = getThen(result);
          if (then) {
            doResolve(then.bind(result), resolve, reject)
            return
          }
          fulfill(result);
        } catch (e) {
          reject(e);
        }
      }
    
      doResolve(fn, resolve, reject);
    }
    

    最後のコードだけを追加しました:私たちはre-use doResolve()メソッドを追加しました!
    現在、ステータスマシンは完了していますが、変化を監視することはできません.最終的な目標は実現します.でもdone()これはもっと簡単なので、まず実現しましょう.ドン()か!私たちの目標はpromiseを実現することですdone(onFulfilled,onRejected)は、次のような特徴を持っています.
  • onFulfilled or onRejectedの1つのみを呼び出す
  • は、
  • を1回のみ呼び出す.
    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise(fn) {
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function fulfill(result) {
        state = FULFILLED;
        value = result;
        handlers.forEach(handle);
        handlers = null;
      }
    
      function reject(error) {
        state = REJECTED;
        value = error;
        handlers.forEach(handle);
        handlers = null;
      }
    
      function resolve(result) {
        try {
          var then = getThen(result);
          if (then) {
            doResolve(then.bind(result), resolve, reject)
            return
          }
          fulfill(result);
        } catch (e) {
          reject(e);
        }
      }
    
      function handle(handler) {
        if (state === PENDING) {
          handlers.push(handler);
        } else {
          if (state === FULFILLED &&
            typeof handler.onFulfilled === 'function') {
            handler.onFulfilled(value);
          }
          if (state === REJECTED &&
            typeof handler.onRejected === 'function') {
            handler.onRejected(value);
          }
        }
      }
    
      this.done = function (onFulfilled, onRejected) {
        // ensure we are always asynchronous
        setTimeout(function () {
          handle({
            onFulfilled: onFulfilled,
            onRejected: onRejected
          });
        }, 0);
      }
    
      doResolve(fn, resolve, reject);
    }
    

    this.doneメソッドが行うこと:handleメソッドを実行し、パラメータを入力します.パラメータは最初が成功時のコールバック関数であり、2番目がreject時のコールバック関数です.
    今、実現しましょう.thenかな~
    this.then = function (onFulfilled, onRejected) {
      var self = this;
      return new Promise(function (resolve, reject) {
        return self.done(function (result) {
          if (typeof onFulfilled === 'function') {
            try {
              return resolve(onFulfilled(result));
            } catch (ex) {
              return reject(ex);
            }
          } else {
            return resolve(result);
          }
        }, function (error) {
          if (typeof onRejected === 'function') {
            try {
              return resolve(onRejected(error));
            } catch (ex) {
              return reject(ex);
            }
          } else {
            return reject(error);
          }
        });
      });
    }
    
  • .そしてdoneと同様に、2つのパラメータを受け入れます.1つ目は成功時コールバック関数onFulfilled、2つ目は失敗時コールバック関数onRejectedです.
  • .thenがチェーン呼び出しを実現できるのはpromiseを返すからである.

  • 最後に私自身は簡単版のpromiseを実現して、コンソールで実行することができて、ここをクリックします:DiPromise
    転載先:https://juejin.im/post/5cc54877f265da03b8585902