Promise實現

Promise實現

這裏超級安利你們去看《深刻淺出nodejs》4.3.2 章節,別問我爲啥總是安利這個,問就真的好看啊!!javascript

Promise 規範

promise的規範有一大堆,可是es6採用了Promise A+ 規範html

相關文檔 Promise A+規範的友情解釋連接 規範說明英文版java

相信你們看完上面給的那個鏈接已經對整個promise的執行過程都有個瞭解了。 這裏總結下node

  • 具備then方法,then()方法繼續返回Promise對象,以實現鏈式調用。
  • Promise操做只會處在3種狀態的一種:未完成態(Pending)、完成態(Fulfilled)和失敗態(Rejected)。
  • Promise的狀態只會出現從未完成態向完成態或失敗態轉化,不能逆反。完成態和失敗態不能互相轉化。
  • Promise的狀態一旦轉化,將不能被更改。
  • 接受完成態、錯誤態的回調方法。在操做完成或出現錯誤時,將會調用對應方法

實現

如下爲菜狗子根據規範約定實(chao)現(xi),有錯歡迎指出。git

大佬們封裝的promise實現測試工具,有實現完成的,能夠用這個測試下是否符合標準。es6

大佬們封裝好的例子想要看更多的,能夠看這裏。菜狗子的實現參考其中belofte.js爲方便理解(就是懶)砍掉了許多類型判斷。github

註釋寫的比我多,講的比我細緻的一篇文章promise

const PENDING = "Pending";
const FULFILLED = "Fulfilled";
const REJECTED = "Rejected";

const root = typeof window === "undefined" ? global : window;
const isFunction = data => typeof data === "function";
const isMyPromise = fn => fn instanceof MyPromise;
const isObject = data =>
  data && (typeof data === "object" || typeof data === "function");
// 由於promise是微任務,咱們儘量的去模擬
const nextTick = (function() {
  if (typeof root.process === "object" && isFunction(root.process.nextTick)) {
    // node環境
    return function(fn) {
      // process.nextTick 是微任務
      root.process.nextTick(fn);
    };
  } else {
    // 瀏覽器環境
    return function(fn) {
      // setTimeout 是宏任務
      root.setTimeout(fn, 0);
    };
  }
})();

// 將promise由中間態到終態
const promiseResolutionProcedure = function(promise, result, async = true) {
  if (promise === result) {
    /** * 由於promise 若是收到的value是一個promise會等待他的結果。 * 因此若是接受到的value是自己就遞歸了。 * * @see https://promisesaplus.com/ 2.3.1 條規定 */
    promise._reject(new TypeError("Chaining cycle detected for promise"));
    return;
  }
  // 若是接收到的是個promise
  if (isMyPromise(result)) {
    switch (result._state) {
      case FULFILLED: {
        nextTick(function() {
          promise._resolve(result._value);
        });
        break;
      }
      case REJECTED: {
        nextTick(function() {
          promise._reject(result._reason);
        });
        break;
      }
      case PENDING: {
        const _resolve = result._resolve;
        const _reject = result._reject;

        result._resolve = function(value) {
          _resolve.call(result, value);
          promise._resolve(value);
        }.bind(result);

        result._reject = function(reason) {
          _reject.call(result, reason);
          promise._reject(reason);
        }.bind(result);
        break;
      }
    }
    return;
  }

  // 若是接受到的是個thenable 對象
  if (isObject(result) && isFunction(result.then)) {
    /** * 屢次調用只有第一次有效 * * @see https://promisesaplus.com/ 2.3.3.3.3 條規定 */
    let flag = false;
    const _resolve = function(value) {
      if (flag) {
        return;
      }
      flag = true;
      promiseResolutionProcedure(promise, value);
    };
    const _reject = function(reason) {
      if (flag) {
        return;
      }
      flag = true;
      promise._reject(reason);
    };
    const thenTemp = function() {
      try {
        result.then(_resolve, _reject);
      } catch (error) {
        _reject(error);
      }
    };
    if (async) {
      nextTick(thenTemp);
    } else {
      thenTemp();
    }
    return;
  }

  promise._resolve(result);
  return;
};

class MyPromise {
  constructor(resolver) {
    if (!isFunction(resolver)) {
      throw new TypeError("Promise resolver undefined is not a function");
    }
    /** @type { PENDING | FULFILLED | REJECTED} */
    this._state = PENDING;

    this._value = undefined;
    this._reason = undefined;

    this._isPromise = true;
    /** * 由於同一個promise能夠被then 屢次。 * 這裏的屢次不是指鏈式調用!!!! * 這裏理解了很久TAT */ 
    this._resolveFnQueues = [];
    this._rejectFnQueuse = [];

    promiseResolutionProcedure(this, { then: resolver }, false);
  }

  _resolve(value) {
    if (this._state !== PENDING) {
      return;
    }
    this._state = FULFILLED;
    this._value = value;
    if (this._resolveFnQueues.length) {
      nextTick(() => {
        this._resolveFnQueues.forEach(cb => cb(value));
        this._resolveFnQueues.length = 0;
        this._rejectFnQueuse.length = 0;
      });
    }
  }

  _reject(reason) {
    if (this._state !== PENDING) {
      return;
    }
    this._state = FULFILLED;
    this._reason = reason;
    if (this._rejectFnQueuse.length) {
      nextTick(() => {
        this._rejectFnQueuse.forEach(cb => cb(reason));
        this._resolveFnQueues.length = 0;
        this._rejectFnQueuse.length = 0;
      });
    }
  }
  // then註冊一個監聽,在這個promise onFulfilled 或者 onRejected
  then(onFulfilled, onRejected) {
    onFulfilled = isFunction(onFulfilled) ? onFulfilled : MyPromise.resolve;
    onRejected = isFunction(onRejected) ? onRejected : MyPromise.reject;
    const chainPromise = new MyPromise(function() {});

    const nextOnFulfilled = function(value) {
      let result;
      try {
        result = onFulfilled(value);
        promiseResolutionProcedure(chainPromise, result);
      } catch (error) {
        chainPromise._reject(error);
      }
    };

    const nextOnRejected = function(reason) {
      let result;
      try {
        result = onRejected(reason);
        promiseResolutionProcedure(chainPromise, result);
      } catch (error) {
        chainPromise._reject(error);
      }
    };

    switch (this._state) {
      case FULFILLED: {
        nextTick(() => {
          nextOnFulfilled(this._value);
        });
        break;
      }
      case REJECTED: {
        nextTick(() => {
          nextOnRejected(this._reason);
        });
        break;
      }
      case PENDING: {
        this._resolveFnQueues.push(nextOnFulfilled);
        this._rejectFnQueuse.push(nextOnRejected);
      }
    }

    return chainPromise;
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  toString() {
    switch (this._state) {
      case PENDING:
        return "Promise { <pending> }";
      case FULFILLED:
        return "Promise { " + this._value + " }";
      case REJECTED:
        return "Promise { <rejected> " + this._reason + " }";
    }
  }

  static resolve(value) {
    return new MyPromise(resolve => resolve(value));
  }

  static reject() {
    return new MyPromise((resolve, reject) => reject(value));
  }
}
複製代碼

補充

async 實現

async 本質就是將 Generator 函數和自動執行器,包裝在一個函數裏瀏覽器

阮一峯大大的文章講的炒雞清楚了。這裏的內容也來自於那篇文章,本人也就溫習實現一下.async

// async 的寫法
async function fn(args){
  // ...
}

// 等同於
function fn(args){ 
  return spawn(function*() {
    // ...
  }); 
}
//spawn 的實現方式
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    var gen = genF();
    step(function() { return gen.next(undefined); });
    function step(nextF) {
      try {
        var next = nextF();
      } catch(e) {
        return reject(e); 
      }
      if(next.done) {
        return resolve(next.value);
      } 
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });      
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
  });
}
複製代碼
相關文章
相關標籤/搜索