請手寫代碼實現一個promise


第一步:promise的聲明

class Promise{
  // 構造器
  constructor(executor){
    // 成功
    let resolve = () => { };
    // 失敗
    let reject = () => { };
    // 立即執行
    executor(resolve, reject);
  }
}

第二步:三個基本狀態(pending、fulfilled、rejected)

class Promise{
  constructor(executor){
    // 初始化state為等待態
    this.state = 'pending';
    // 成功的值
    this.value = undefined;
    // 失敗的原因
    this.reason = undefined;
    let resolve = value => {
      // state改變,resolve調用就會失敗
      if (this.state === 'pending') {
        // resolve調用后,state轉化為成功態
        this.state = 'fulfilled';
        // 儲存成功的值
        this.value = value;
      }
    };
    let reject = reason => {
      // state改變,reject調用就會失敗
      if (this.state === 'pending') {
        // reject調用后,state轉化為失敗態
        this.state = 'rejected';
        // 儲存失敗的原因
        this.reason = reason;
      }
    };
    // 如果executor執行報錯,直接執行reject
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
}

第三步:then方法(兩個參數:onFulfilled,onRejected)

onFulfilled,onRejected如果他們是函數,

則必須分別在fulfilled,rejected后被調用,value或reason依次作為他們的第一個參數

class Promise{
  constructor(executor){...}
  // then 方法 有兩個參數onFulfilled onRejected
  then(onFulfilled,onRejected) {
    // 狀態為fulfilled,執行onFulfilled,傳入成功的值
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    // 狀態為rejected,執行onRejected,傳入失敗的原因
    if (this.state === 'rejected') {
      onRejected(this.reason);
    };
  }
}

第四步:異步的實現

當resolve在setTomeout內執行,then時state還是pending等待狀態

我們就需要在then調用的時候,將成功和失敗存到各自的數組,一旦reject或者resolve,就調用它們

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 成功存放的數組
    this.onResolvedCallbacks = [];
    // 失敗存放法數組
    this.onRejectedCallbacks = [];
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // 一旦resolve執行,調用成功數組的函數
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    let reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        // 一旦reject執行,調用失敗數組的函數
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    if (this.state === 'rejected') {
      onRejected(this.reason);
    };
    // 當狀態state為pending時
    if (this.state === 'pending') {
      // onFulfilled傳入到成功數組
      this.onResolvedCallbacks.push(()=>{
        onFulfilled(this.value);
      })
      // onRejected傳入到失敗數組
      this.onRejectedCallbacks.push(()=>{
        onRejected(this.reason);
      })
    }
  }
}

第五步:鏈式調用

new Promise().then().then()

這就是鏈式調用,用來解決回調地獄

1、為了達成鏈式,我們默認在第一個then里返回一個promise,叫promise2,將它傳遞下一個then中。

2、我們需要在then中return一個參數,這個就叫x,x如果是promise,則取它的結果,作為promise2成功的結果。如果是普通值,直接作為promise2成功的結果,判斷x的函數叫resolvePromise,帶四個參數(promise2,x,resolve,reject)

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    let reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    // 聲明返回的promise2
    let promise2 = new Promise((resolve, reject)=>{
      if (this.state === 'fulfilled') {
        let x = onFulfilled(this.value);
        // resolvePromise函數,處理自己return的promise和默認的promise2的關系
        resolvePromise(promise2, x, resolve, reject);
      };
      if (this.state === 'rejected') {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(()=>{
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        })
        this.onRejectedCallbacks.push(()=>{
          let x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        })
      }
    });
    // 返回promise,完成鏈式
    return promise2;
  }
}

第六步:實現resolvePromise函數

function resolvePromise(promise2, x, resolve, reject){
  // 循環引用報錯
  if(x === promise2){
    // reject報錯(檢測到promise的鏈接循環
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // 防止多次調用
  let called;
  // x不是null 且x是對象或者函數
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+規定,聲明then = x的then方法
      let then = x.then;
      // 如果then是函數,就默認是promise了
      if (typeof then === 'function') { 
        // 就讓then執行 第一個參數是this   后面是成功的回調 和 失敗的回調
        then.call(x, y => {
          // 成功和失敗只能調用一個
          if (called) return;
          called = true;
          // resolve的結果依舊是promise 那就繼續解析
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 成功和失敗只能調用一個
          if (called) return;
          called = true;
          reject(err);// 失敗了就失敗了
        })
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) {
      // 也屬於失敗
      if (called) return;
      called = true;
      // 取then出錯了那就不要在繼續執行了
      reject(e); 
    }
  } else {
    resolve(x);
  }
}

第七步:解決一些問題

1、onFulfilled或onRejected不能同步被調用,必須異步調用。我們就用setTimeout解決異步問題

2、onFulfilled返回一個普通的值,成功時直接等於 value => value

3、onRejected返回一個普通的值,失敗時如果直接等於 value => value,則會跑到下一個then中的onFulfilled中,所以直接扔出一個錯誤 reason => throw err

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    let reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    // onFulfilled如果不是函數,就忽略onFulfilled,直接返回value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected如果不是函數,就忽略onRejected,直接扔出錯誤
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    let promise2 = new Promise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        // 異步
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'rejected') {
        // 異步
        setTimeout(() => {
          // 如果報錯
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          // 異步
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          // 異步
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
      };
    });
    // 返回promise,完成鏈式
    return promise2;
  }
}

 

祝賀:一個promise就在自己筆下誕生啦~~

 

參考資料:

https://github.com/xieranmaya/blog/issues/3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM