js實現一個Promises/A+ 規範的Promise

1.代碼實現

// promise 三個狀態
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED ='rejected';
class Promisex {
  constructor(fn){
    if(typeof fn !== 'function') throw new Error('Promisex must accept a function')
    this.state = PENDING;//初始狀態
    this.value = null;//返回的信息/拒絕的原因

    this.onFulfilledQueues = [];//存儲fulfilled狀態對應onFulfilled函數隊列
    this.onRejectedQueues = [];//存儲rejected狀態對應onRejected函數隊列

    // value成功態時接收的終值
    const resolve=(value) => {
      if(value instanceof Promisex){
        return value.then(resolve,reject);
      }else{
        //加setTimeout原因:
        //實踐中要確保 onFulfilled 和 onRejected 方法異步執行,
        setTimeout(() => {
          if(this.state!==PENDING) return;
          this.state = FULFILLED;
          this.value = value;
          let cb;
          while (cb = this.onFulfilledQueues.shift()) {
            cb(value)
          }
        });
      }
    }

    // reason失敗態時接收的拒因
    const reject=(reason)=>{
      setTimeout(() => {
        if(this.state!==PENDING) return;
        this.state = REJECTED;
        this.value = reason;
        let cb;
        while (cb = this.onRejectedQueues.shift()) {
          cb(reason)
        }
      });
    }

    //執行
    try {
      fn(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }

  //註冊fulfilled狀態/rejected狀態對應的回調函數
  //then返回一個新的promise對象
  //then中需要處理三種狀態
  then(onFulfilled, onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

    let newPromise;
    if(this.state===FULFILLED){//成功狀態
      return newPromise=new Promisex((resolve,reject)=>{
        setTimeout(() => {
          try {
            let x=onFulfilled(this.value);//onFulfilled有返回值
            resolvePromise(newPromise,x,resolve,reject);
          } catch (e) {
            reject(e)
          }
        });
      })
    }
    if(this.state===REJECTED){//失敗狀態
      return newPromise=new Promisex((resolve,reject)=>{
        setTimeout(() => {
          try {
            //不論 promise1 被 reject 還是被 resolve 時 promise2 都會被 resolve,只有出現異常時纔會被 rejected。
            let x=onRejected(this.value);//onRejected有返回值
            resolvePromise(newPromise,x,resolve,reject);
          } catch (e) {
            reject(e)
          }
        });
      })
    }
    if(this.state===PENDING){//等待狀態
      //將onFulfilled/onRejected收集暫存到隊列中
      return newPromise=new Promisex((resolve,reject)=>{
        this.onFulfilledQueues.push((value)=>{
          try {
            let x=onFulfilled(value);
            resolvePromise(newPromise,x,resolve,reject);
          } catch (e) {
            reject(e)
          }
        })
        this.onRejectedQueues.push((reason)=>{
          try {
            let x=onRejected(reason);
            resolvePromise(newPromise,x,resolve,reject);
          } catch (e) {
            reject(e);
          }
        })
      })
    }
  }

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

  finally(cb){
    return this.then(value=>{
      Promisex.resolve(cb()).then(()=>value)
    },err=>{
      Promisex.resolve(cb()).then(()=>{throw err})
    })
  }

  static resolve(value){
    if(value instanceof Promisex) return value;
    return new Promisex(resolve=>resolve(value));
  }

  static reject(reason){
    return new Promisex((undefined,reject)=>reject(reason));
  }

  static all(list){
    return new Promisex((resolve,reject)=>{
      let values=[];
      let count=0;
      for(let [i,p] of list.entries()){
        this.resolve(p).then(res=>{
          values[i]=res;
          count++;
          if(count === list.length) resolve(values);
        },err=>{
          reject(err);
        })
      }
    })
  }

  static race(list){
    return new Promisex((resolve,reject)=>{
      for(let p of list){
        this.resolve(p).then(res=>{
          resolve(res);
        },err=>{
          reject(err);
        })
      }
    })
  }
}


function resolvePromise(promise2,x,resolve,reject){
  if(promise2===x){// 如果從onFulfilled中返回的x 就是promise2 就會導致循環引用報錯
    return reject(new TypeError('循環引用'))
  }

  let called = false;// 避免多次調用
  // * 1.promise對象
  // * 2.thenable對象/函數
  // * 3.普通值
  if(x instanceof Promisex){
    if(x.state===PENDING){
      x.then(y=>{
        resolvePromise(promise2,y,resolve,reject);
      },reason=>{
        reject(reason)
      })
    }else{
      x.then(resolve,reject);
    }
  }else if(x&&(typeof x==='object'||typeof x==='function')){
    try {
      //是否是thenable對象(具有then方法的對象/函數)
      let then = x.then;
      if(typeof then==='function'){
        then.call(x,y=>{
          if(called)return;
          called=true;
          resolvePromise(promise2,y,resolve,reject);
        },reason=>{
          if(called)return;
          called=true;
          reject(reason);
        })
      }else{//說明是一個普通對象
        resolve(x)
      }
    } catch (e) {
      if(called)return;
      called=true;
      reject(e)
    }
  }else{
    resolve(x);
  }
}

//使用promises-aplus-tests測試
Promisex.deferred = function() {
  // 延遲對象
  let defer = {};
  defer.promise = new Promisex((resolve, reject) => {
    defer.resolve = resolve;
    defer.reject = reject;
  });
  return defer;
};
module.exports = Promisex;

最後執行promises-aplus-tests ./xxx.js進行測試

2.例子

//不論 promise1 被 reject 還是被 resolve 時 promise2 都會被 resolve,只有出現異常時纔會被 rejected。
new Promisex((resolve, reject)=>{
  setTimeout(() => {
    reject(2)
  }, 2000);
}).then(res=>{
  console.log(res);
  return res+1;
},e=>{
  return e;//2
}).then(res=>{
  console.log(res)//2
},e=>{
  return e;
})

3.參考

Promise詳解與實現(Promise/A+規範)
Promises/A+規範(英文)
【翻譯】Promises/A+規範
Javascript異步編程的4種方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章