ES6中的Promise實現原理

說明

由於手寫實現Promise無法區別宏任務與微任務,因此這裏的實現源碼只是爲了理解。

Promise規範要點

  • Promise是一個類,接收一個函數作爲參數,該函數稱爲執行器,創建Promise對象時,執行器會立刻執行;
  • Promise有三種狀態,分別爲pending等待、fulfilled成功、rejected失敗;其中pending會轉換爲fulfilled或者rejected,一旦狀態更新,則無法再更新狀態;
  • 執行器函數接收兩個參數,分別爲resolve函數和reject函數,只是用於更新Promise對象的狀態,並可以將數據傳遞下去;reject的參數並不是錯誤,只有用throw語句纔會拋出錯誤。拋出錯誤的時候,由執行器函數或回調函數內部的try-catch語句來捕獲。由於我們的reject函數放在了catch語句裏,因此then方法的rejected回調函數使用throw來向下傳遞數據。
  • then()方法內部需要判斷狀態,狀態成功則調用onFulfilled函數,狀態失敗則調用onRejected函數。
  • 異步情況的處理,需要存儲then方法內部的回調函數,在異步任務結束時,調用resolve或reject後,同時調用相應的回調函數
// 定義狀態常量,在編輯器中可以得到代碼提示信息;
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    // try-catch捕獲執行器異常
    try {
      executor(this.resolve, this.reject);
    } catch(e) {
      this.reject(e);
    }
  }
  // 定義實例屬性,用於表示Promise的狀態
  _status = PENDING;
  // 成功之後的值
  _value = undefined;
  // 失敗之後的值
  _err = undefined;
  // 用於存儲成功回調函數的隊列,實現給同一個Promise用then()註冊多個回調函數
  onSuccess = [];
  // 用於存儲失敗回調函數的隊列,實現給同一個Promise用then()註冊多個回調函數
  onFail = [];
  // 之所以定義爲箭頭函數,是爲了在使用resolve()調用時,內部的this指向MyPromise的實例
  resolve = (value) => {
    // 判斷狀態,如果不是PENDING,則返回;
    if(this._status !== PENDING) return;
    // 更新狀態
    this._status = FULFILLED;
    // 保存value值
    this._value = value;
    // 判斷成功回調是否存在,如果存在,則調用
    // this.onSuccess && this.onSuccess(this._value);
    while(this.onSuccess.length) {
      this.onSuccess.shift()();
    }
  }
  reject = (err) => {
    // 判斷狀態,如果不是PENDING,則返回;
    if(this._status !== PENDING) return;
    // 更新狀態
	this._status = REJECTED;
	// 保存err
	this._err = err;
	// 判斷失敗回調是否存在,如果存在,則調用
	// this.onFail && this.onFail(this._err);
	while(this.onFail.length) {
      this.onFail.shift()();
    }
  }
  // 實現then方法
  then(successCallback, failCallback) {
    // 解決then()方法可選參數的問題;
    successCallback = successCallback ? successCallback: value => value;
    // 用throw語句將異常傳遞下去,throw語句用來拋出異常,可以將throw關鍵字後面表達式的值拋出
    failCallback = failCallback ? failCallback: err => { throw err; }
    // 創建一個Promise對象並返回,實現鏈式調用;
    let my_promise = new MyPromise((resolve, reject) => {
	  // 判斷狀態
      if(this._status === FULFILLED) {
        setTimeout(() => {
          // 捕獲回調函數中的異常
          try {
            // 創建變量來接收成功回調函數的返回值;
            let x = successCallback(this._value);
            // 判斷x值是否是Promise對象,如果是普通值,則直接resolve或reject
            // 否則需要查看Promise對象的值,再調用resolve或reject
            // 調用resolvePromise(),將x的值傳遞出去;
            // 由於三種狀態下都需要用到這個函數,所以這裏的resolvePromise()方法抽象爲公共函數
            // 傳入my_promise是爲了對比my_promise與x是否相同,相同則表示出現Promise循環調用,需要拋出異常
            // my_promise對象無法在同步的情況下拿到
            // 因此將整個代碼放到setTimeout()中來獲取my_promise對象
            resolvePromise(my_promise, x, resolve, reject);
          } catch(e) {
            reject(e);
          }
        }, 0);
      } else if(this._status === REJECTED) {
          setTimeout(() => {
            try {
              let x = failCallback(this._err);
              resolvePromise(my_promise, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          }, 0);
      } else {
        // 處理異步情況,此時爲PENDING狀態,就將成功狀態和失敗狀態的回調函數存儲起來
        this.onSuccess.push(() => {
		  setTimeout(() => {
            try {
              let x = successCallback(this._value);
            } catch(e) {
              reject(e);
            }
          }, 0);
        });
        this.onFail.push(() => {
		  setTimeout(() => {
            try {
              let x = failCallback(this._value);
            } catch(e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
   	return my_promise;
  }
  // 定義靜態方法all()
 static all(arr) {
   // 聲明一個數組,用來存儲arr中所有操作的結果
   let result = [];
   // 聲明一個變量index,用來判斷是否所有異步操作都已完成,如果異步操作未完成,index值小於arr.length
   let index = 0;
   return new MyPromise((resolve, reject) => {
     // 聲明addData函數,用於將操作的結果存儲到result數組
     function addData(key, value) {
       result[key] = value;
       index ++;
       if(index === arr.length) {
         resolve(result);
       }
     }
     for(let i = 0; i < arr.length; i ++) {
       let current = arr[i];
       if(current instanceof MyPromise) {
         // 當前arr元素爲Promise對象
         current.then((value) => {
           addData(value);
         }, (err) => {
           reject(err);
         })
       } else {
         // 當前arr元素不是Promise對象
         addData(i, arr[i]);
       }
     }
   })
 }
 // 定義靜態方法resolve
 static resolve(value) {
   // 如果傳入的value是Promise對象,則原樣返回,否則返回新的Promise對象,將value傳遞
   if(value instanceof MyPromise) {
     return value;
   } else {
     return new MyPromise((resolve) => {
       resolve(value);
     })
   }
 }
 // 定義實例finally方法,不管Promise對象狀態如何,finally的回調函數一定會執行
 finally(callback) {
   // 返回一個Promise對象
   return this.then((value) => {
     return MyPromise.resolve(callback()).then(() => value);
   }, (err) => {
     return MyPromise.resolve(callback()).then(() => { throw err; });
   })
 }
 // 定義實例catch方法
 catch(failCallback) {
   // 調用then方法,只傳遞onRejected狀態的回調即可
   return this.then(undefined, failCallback);
 }
}
function resolvePromise(my_promise, x, resolve, reject) {
  if(my_promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  if(x instanceof MyPromise) {
    // x是Promise對象,則調用then方法查看值,並利用resolve或reject傳遞
    /* x.then((value) => {
      resolve(value)
    }, (err) => {
      reject(err);
    }); */
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章