Promise 源碼和分析

Promise 介紹

什麼是 Promise?解決哪些問題?node

  • 回調地獄,代碼很差維護,錯誤處理很是的麻煩,不能統一處理錯誤
  • 處理多個請求併發問題

ES6 將其寫進了語言標準,統一了用法,原生提供了 Promise 對象。npm

  • Promise 是一個對象,使用的時候 new 便可
  • Promise 在 new 的時候傳入一個執行器,即 executor,這個函數在 new Promise 時當即執行
  • Promise 的 executor 函數有兩個參數,一個是 resolve,一個是 reject。resolve 讓 Promise 由 pending 等待態變成 fulfilled 成功態,reject 讓 Promise 由 pending 等待態變成 rejected 失敗態。resolve 和 reject 是 Promise 提供的。
  • Promise 的 3 個狀態:pending 等待態、fulfilled 成功態、rejected 失敗態
  • Promise 的 resolve 或者 reject 只能調用一次,一旦成功以後不能再失敗,一旦失敗以後不能再成功
  • 每一個 Promise 實例有個 then() 方法,該方法內部傳遞兩個函數,第一個是成功回調函數,第二個是失敗回調函數
  • 讓一個 Promise 失敗能夠調用 reject,也能夠拋出一個錯誤
  • Promise 的 then() 方法返回的仍是一個新的 Promise,因此 then() 以後還能夠再 then

PromiseA+.png

// 定義三種狀態
  const REJECTED = 'REJECTED';
  const FULFILLED = 'FULFILLED';
  const PENDING = 'PENDING';

  /**
   * 判斷一個變量是不是 Promise 的函數
   * 1. 是不是對象而且不是 null
   * 2. 是否有 then 屬性而且 then 是一個方法
   * @param {*} val 
   */
  function isPromise(val) {
    if (typeof val === 'object' && val !== null || typeof val === 'function') {
      return typeof val.then === 'function';
    }
    return false;
  }

  console.log('--------------------- MY Promise ---------------------');

  // 實現鏈式調用
  /**
   * 處理 then 中的成功、失敗回調函數返回值是不是 Promise 的狀況
   * @param {*} promise2 參見 Promise A+ 規範說明 new Promise
   * @param {*} x 是成功、失敗回調的返回值
   */
  const resolvePromise = (promise2, x) => {
    const { resolve, reject } = promise2;
    // 這裏的判斷是可能本身的 Promise 要和別人的 Promise 混用,可能不一樣的 Promise 庫之間相互調用
    // 若是 new 出來的 Promise2 和 x 是同一個,那麼 x 永遠不能成功或者失敗,因此卡死了,直接報錯
    if (promise2 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    // 須要判斷 x 的狀態,判斷 x 是否是 Promise
    // 先判斷是否是對象或者函數
    if (typeof x === 'object' && x !== null || typeof x === 'function') {
      let called; // 這裏考慮別人的 Promise 是否健壯,不健壯須要再判斷一下,調用了成功就不能失敗,調用的失敗就不能成功。不能屢次調用成功或者失敗
      try {
        const then = x.then; // 內部可能拋出錯誤
        // 若是 then 不是函數就說明是一個普通值,直接返回 x 
        if (typeof then !== 'function') resolve(x); // 好比:{ then: 'mhq' }
        else {
          // 這裏不要再次調用 x.then 是防止取 then 的時候報錯,萬一 then 方法裏面拋出了一個錯誤呢?
          then.call(x, y => {
            if (called) return;
            called = true;
            // 若是 x 是一個 Promise,那麼就繼續解析成功的值
            resolvePromise(promise2, y);
          }, f => {
            if (called) return;
            called = true;
            reject(f); // 直接調用 r 做爲失敗的結果
          });
        }
      } catch(err) {
        if (called) return;
        called = true;
        reject(err);
      }
    } else {
      resolve(x);
    }
  };

  class Promise {
    constructor(executor) {
      // Promise 的狀態
      this.status = PENDING;
      // 成功後的值
      this.value = undefined;
      // 失敗後的值
      this.reason = undefined;

      // 成功回調函數,發佈訂閱
      this.onResolvedCallbacks = [];
      // 失敗回調函數,發佈訂閱
      this.onRejectedCallbacks = [];

      /**
       * Promise 內部提供的 resolve,讓 Promise 的狀態變成成功態,並讓成功回調執行
       * @param {*} value 
       */
      const resolve = value => {
        if (value instanceof Promise) {
          return value.then(resolve, reject);
        }
        if (this.status === PENDING) {
          this.status = FULFILLED;
          this.value = value;
          this.onResolvedCallbacks.forEach(fn => fn());
        }
      };

      /**
       * Promise 內部提供的 reject,讓 Promise 的狀態變成失敗態,並讓失敗回調執行
       * @param {*} reason 
       */
      const reject = reason => {
        if (this.status === PENDING) {
          this.status = REJECTED;
          this.reason = reason;
          this.onRejectedCallbacks.forEach(fn => fn());
        }
      };
      // try + catch 只能捕獲同步異常
      try {
        executor(resolve, reject);
      } catch(err) {
        reject(err);
      }
    }

    // 只要 x 是一個普通值,就會讓一個 Promise 變成成功態
    // 這個 x 有多是一個 Promise,須要菜喲個這個 Promise 的狀態
    then(onFulfilled, onRejected) {
      // 好比.then().then().then(() => {}); 這種調用,對可選參數的處理,透傳
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
      onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

      const promise2 = new Promise((resolve, reject) => { // 一旦 new 則當即執行
        if(this.status === FULFILLED) {
          setTimeout(() => {
            promise2.resolve = resolve;
            promise2.reject = reject;
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x);
            } catch (err) {
              reject(err);
            }
          }, 0);
        }
        if (this.status === REJECTED) {
          setTimeout(() => {
            promise2.resolve = resolve;
            promise2.reject = reject;
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x);
            } catch (err) {
              reject(err);
            }
          }, 0);
        }
        if (this.status === PENDING) { // 自己是異步的
          this.onResolvedCallbacks.push(() => {
            setTimeout(() => { // 這裏須要加上,不加上跑測試跑不通
              promise2.resolve = resolve;
              promise2.reject = reject;
              try {
                const x = onFulfilled(this.value);
                resolvePromise(promise2, x);
              } catch (err) {
                reject(err);
              }
            });
          });
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              promise2.resolve = resolve;
              promise2.reject = reject;
              try {
                const x = onRejected(this.reason);
                resolvePromise(promise2, x);
              } catch (err) {
                reject(err);
              }
            });
          });
        }
      });

      return promise2;
    }
    /**
     * Promise 中的 catch 指代的就是 then 沒有成功回調的一個別名而已
     * @param {*} errCallback 
     */
    catch(errCallback) {
      return this.then(null, errCallback);
    }
  }

  // 不管如何都會執行,把上一個 then 的結果向下傳遞,若是 finally 中返回了一個 Promise 會等待這個 Promise 執行完成後繼續執行
  Promise.prototype.finally = function(callback) {
    return this.then(val => {
      return Promise.resolve(callback()).then(() => val);
    }, (err) => {
      return Promise.resolve(callback()).then(() => { throw err; });
    });
  };

  // npm install promises-aplus-tests -g
  // promises-aplus-tests promise.js
  // 測試本身寫的 Promise 是否符合規範的包
  Promise.deferred = () => {
    const dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
      dfd.resolve = resolve;
      dfd.reject = reject;
    });
    return dfd;
  };

  /**
   * Promise.resolve 他會等待裏面的 Promise 執行成功
   * @param {*} val 
   */
  Promise.resolve = val => {
    return new Promise((resolve) => {
      resolve(val);
    });
  };

  /**
   * Promise.reject 不會等待參數中的 Promise 執行完畢
   */
  Promise.reject = () => {
    return new Promise((_, reject) => {
      reject(val);
    });
  };

  /**
   * Promise.all 方法表示等待全部的 Promise 所有成功後纔會執行回調,若是有一個 Promise 失敗則 Promise 就失敗了
   * @param {*} promises 
   */
  Promise.all = promises => {
    return new Promise((resolve, reject) => {
      const res = [];
      let count = 0;

      const resolveRes = (index, data) => {
        res[index] = data;
        if (++count === promises.length) {
          resolve(res);
        }
      };

      for(let i = 0; i < promises.length; i++) {
        const current = promises[i];
        if (isPromise(current)) {
          current.then((data) => {
            resolveRes(i, data);
          }, (err) => {
            reject(err);
          });
        } else {
          resolveRes(i, current);
        }
      }
    });
  }

  /**
   * Promise.race 賽跑,誰是第一個完成的,就用他的結果,若是是失敗這個 Promise 就失敗,若是第一個是成功就是成功
   * @param {*} promises 
   */
  Promise.race = (promises) => {
    return new Promise((resolve, reject) => {
      for(let i = 0; i < promises.length; i++) {
        let current = promises[i];
        if (isPromise(current)) {
          current.then(resolve, reject);
        } else {
          resolve(current);
          break;
        }
      }
    });
  }

  // 專門給 node 的 api 作的 promisify 方法,如 fs.readFile
  Promise.promisify = fn => {
    return (...arg) => {
      return new Promise((resolve, reject) => {
        fn(...arg, (err, data) => {
          if (err) reject(err);
          resolve(data);
        });
      });
    }
  };

  module.exports = Promise;
相關文章
相關標籤/搜索