Es6 Promise 實現

江紀雲發表於2019-05-30
// promise 狀態
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const isFn = fn => typeof fn === 'function';

class Promise {
  constructor(resolver) {
    if (!isFn(resolver)) {
      throw new TypeError('Promise 引數必須是函式');
    }

    //狀態和值
    this._status = PENDING;
    this._value;

    //執行佇列
    this._fulfilledQueue = [];
    this._rejectedQueue = [];

    //繫結this
    this._resolve = this._resolve.bind(this);
    this._reject = this._reject.bind(this);

    resolver(this._resolve, this._reject);
  }

  _resolve(val) {
    if (this._status !== PENDING) return;
    //因為Promise裡面的函式是同步的,then是非同步的
    setTimeout(() => {
      this._status = FULFILLED;
      this._value = val;
      let cb;
      while (cb = this._fulfilledQueue.shift()) {
        cb(this._value)
      }
    }, 0);
  }

  _reject(val) {
    if (this._status !== PENDING) return;
    //因為Promise裡面的函式是同步的,then是非同步的
    setTimeout(() => {
      this._status = REJECTED;
      this._value = val;
      let cb;
      while (cb = this._rejectedQueue.shift()) {
        cb(this._value)
      }
    }, 0);
  }

  // then 引數型別:
  //(1)值 
  //(2)函式, 返回值或者Promise
  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      // 返回一個 resolve 或者 reject 函式
      let runThen = (callback, resolver) => {
        return function (val) {
          try {
            if (!isFn(callback)) { // 判斷 onFulfilled、onRejected 是否是一個 function,否的話直接透傳value
              resolver(val)
            } else {
              const res = callback(val); // 判斷函式返回值是否是一個 Promise
              if (res instanceof Promise) {
                res.then(resolve, reject);
              } else {
                resolver(res)
              }
            }
          } catch (err) {
            reject(err);
          }
        }
      }
      //判斷當前的status
      switch (this._status) {
        case FULFILLED:
            runThen(onFulfilled, resolve)(this._value);
          break;
        case REJECTED:
            runThen(onRejected, reject)(this._value);
          break;
        case PENDING: //狀態未變,將函式新增到執行佇列,等待狀態改變後執行
          this._fulfilledQueue.push(runThen(onFulfilled, resolve));
          this._rejectedQueue.push(runThen(onRejected, reject));
          break;
      }
    });
  }

  static resolve(promise) {
    // 判斷 promise 是否是一個 promise 物件
    if (promise instanceof Promise) {
      return promise;
    } else {
      return new Promise(resolve => {
        resolve(promise);
      });
    }
  }

  static reject(err){
    return new Promise((resolve, reject) => reject(err));
  }

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

  finally(callback) {
    return this.then(
      value => Promise.resolve(callback()).then(() => value),
      reason => Promise.resolve(callback()).then(() => { throw reason })
    );
  }

  static race(pmList) {
    return new Promise((resolve, reject) => {
      pmList.forEach(item => {
        // item 可能不是 promise,所以先呼叫 promise.resolve
        Promise.resolve(item).then(val => {
          resolve(val);
        }, err => {
          reject(err);
        });
      })
    })
  }

  static all(pmList) {
    return new Promise((resolve, reject) => {
      let resolveArr = [];
      let count = 0;
      pmList.forEach((item, index) => {
        // item 可能不是 promise,所以先呼叫 promise.resolve
        Promise.resolve(item).then(val => {
          resolveArr[index] = val;
          count++;
          if (count === pmList.length) {
            resolve(resolveArr);
          }
        }, err => {
          reject(err);
        });
      })
    })
  }
}
複製程式碼

相關文章