promise的原理以及實(shí)現(xiàn)(一),看不懂來找我

Promise主要作用:把異步操作變?yōu)楹唵慰煽?,在完成異步操作后通過then可以做你想的操作。解決了地獄回調(diào)的痛點(diǎn)。

本篇實(shí)現(xiàn)了Promise的基本方法resolvereject .then()、.then().then()鏈?zhǔn)秸{(diào)用。

一、Promise概念原理:

遵循Promise/A+規(guī)則:https://promisesaplus.com
它代表了一個異步操作的最終完成或者失敗。只接受一個函數(shù)(內(nèi)部叫executor自動執(zhí)行),并且函數(shù)有兩個參數(shù):resolve(成功)、reject(失?。?。
記住,只要聲明了一個Promise即new Promise(),他就會立即執(zhí)行!
執(zhí)行時內(nèi)部有三種狀態(tài),pending表示等待中、fulfilled表示成功、rejected表示失敗。
它的結(jié)果是一個明確的狀態(tài):

  • pending => fulfilled
  • pending => rejected
    聽起來它的內(nèi)部有點(diǎn)像薛定諤的貓
    只要成功了,永遠(yuǎn)都是成功,失敗了永遠(yuǎn)都是失敗,狀態(tài)已經(jīng)改變就不能再改變。

二、基本使用:

1、一般我們使用它都需要與.then()方法一起食用,其中.then()也是接受兩個可選的參數(shù):fulfilled(promise成功時候的回調(diào)函數(shù))、rejected(promise失敗時候的回調(diào)函數(shù))。如果傳入then的參數(shù)不是一個函數(shù),則會忽略它。

// promise.then語法
new Promise((resolve, reject) => {
  reject(1);  // 或是resolve(1)
}).then(res => {
  console.log('fulfillment', res);
}, err => {
  console.log('rejection', err);
});

// resolve表示成功,可以通過.then()方法獲取到成功的結(jié)果
new Promise((resolve, reject) => {
  resolve(1);
}).then(res => console.log(res));  // 1


// reject表示失敗,使用.catch()方法獲取到失敗錯誤原因
new Promise((resolve, reject) => {
  reject(2);
}).catch(err => console.log(err));   // 2

2、支持鏈?zhǔn)绞褂?/p>

new Promise((resolve, reject) => {
  resolve(1);
}).then(res => res).then(res => console.log(res));  // 1

//如果不return一個結(jié)果,默認(rèn)返回的是一個undefind
new Promise((resolve, reject) => {
  resolve(1);
}).then(res => {}).then(res => console.log(res));   // undefind

三、實(shí)現(xiàn)環(huán)節(jié)

1、初步結(jié)構(gòu)

按照上面我們已經(jīng)知道Promise內(nèi)部的一些內(nèi)容,寫一個方法架構(gòu)出來。
它需要有:三種狀態(tài)(pending、fulfilled、rejected)、默認(rèn)值(成功/失敗的默認(rèn)返回值undefind)、默認(rèn)方法(executor、resolve、reject 、then)先寫上去。

class MyPromise {

  /**
   * 內(nèi)部三種狀態(tài)
   * @type {string}
   */
  static pending = 'pending';
  static fulfilled = 'fulfilled';
  static rejected = 'rejected';

  constructor(executor) {
    this.status = MyPromise.pending;  //默認(rèn)的狀態(tài)是pending
    this.susecessValue = undefined;  // 成功默認(rèn)返回是undefined
    this.failedValue = undefined;  // 失敗默認(rèn)返回是undefined
    executor(this.resolve, this.reject);  //自動執(zhí)行
  }

  /**
   * 成功的方法
   * @param res
   */
  resolve = (res) => {

  };

  /**
   * 失敗的方法
   * @param err
   */
  reject = (err) => {

  };

  /**
   * 獲取成功結(jié)果的then方法
   * @param onResolved
   * @param onRejected
   */
  then = (onResolved, onRejected) => {

  };
}
2、接著把resolve、reject 、then方法補(bǔ)充完整,很簡單,只需要判斷它的狀態(tài)。resolve把狀態(tài)改為fulfilled成功,同時把值存起來;reject 把狀態(tài)改為rejected,同時把值存起來。then方法分別判斷,是成功還是還是失敗,去運(yùn)行then((res)=>{})傳進(jìn)來的方法就可以。
class MyPromise {

  static pending = 'pending';
  static fulfilled = 'fulfilled';
  static rejected = 'rejected';

  constructor(executor) {
    this.status = MyPromise.pending;
    this.susecessValue = undefined;
    this.failedValue = undefined;
    executor(this.resolve, this.reject);
  }

  resolve = (res) => {
    if (this.status === 'pending') {
      this.susecessValue = res;
      this.status = MyPromise.fulfilled;  // 成功了直接切換狀態(tài),因?yàn)閠hen()方法需要使用狀態(tài)
    }
  };

  reject = (err) => {
    if (this.status === 'pending') {
      this.failedValue = err;
      this.status = MyPromise.rejected; // 失敗了直接切換狀態(tài),因?yàn)閠hen()方法需要使用狀態(tài)
    }
  };

  then = (onResolved, onRejected) => {
    if (this.status === 'fulfilled' && onResolved) {
      onResolved(this.susecessValue);
    }
    if (this.status === 'rejected' && onRejected) {
      onRejected(this.failedValue);
    }
  };
}

到這里算是最基本的一個promise完成了,我們來運(yùn)行看一下:

new MyPromise((resolve, reject) => {
  resolve('成功1111');
}).then(res => {
  console.log('fulfillment', res);  // fulfillment 成功1111
}, err => {
  console.log('rejection', err);
});
new MyPromise((resolve, reject) => {
  reject('失敗222');
}).then(res => {
  console.log('fulfillment', res);
}, err => {
  console.log('rejection', err);  //  rejection 失敗222
});

通過運(yùn)行是沒有問題的,到這里我們算是完成了第一個小目標(biāo)。
但是如果此時我們碰到了setTimeout就會有很大麻煩。比如resolve是寫在setTimeout里的:

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('測試');
  }, 3000);
}).then(res => {
  console.log('fulfillment', res);
}, err => {
  console.log('rejection', err);
});

我們會發(fā)現(xiàn)該方法并不會運(yùn)行,原因很簡單,因?yàn)榇藭r的resolve('測試')方法被延遲了3秒才執(zhí)行,然而我們寫的then是沒有被延遲的,所以這時候,then()方法會先執(zhí)行,但是因?yàn)槲覀兊?code>then內(nèi)部寫了this.status狀態(tài)判斷(此時resolve還沒有把默認(rèn)的狀態(tài)statuspending改為fulfilled)所以它不執(zhí)行內(nèi)部的任何一個方法。

3、增加支持異步(重點(diǎn)難點(diǎn))

我們可以把then(x = > x, y=> x)方法中的兩個參數(shù)(都是函數(shù))先存起來。等待resolve或者reject的時候再運(yùn)行。

class MyPromise {

  static pending = 'pending';
  static fulfilled = 'fulfilled';
  static rejected = 'rejected';

  constructor(executor) {
    this.onFulfilledFoo = [];  // 存儲成功的方法
    this.onRejectedFoo = [];  // 存儲失敗的方法
    this.status = MyPromise.pending;
    this.susecessValue = undefined;
    this.failedValue = undefined;
    executor(this.resolve, this.reject);
  }

  resolve = (res) => {
    if (this.status === 'pending') {
      this.susecessValue = res;
      this.status = MyPromise.fulfilled; 
      // resolve 時運(yùn)行我們存儲過的對應(yīng)onResolved方法,注意要把參數(shù)傳遞給fn
      this.onFulfilledFoo.forEach(fn => fn(res)); 
    }
  };

  reject = (err) => {
    if (this.status === 'pending') {
      this.failedValue = err;
      this.status = MyPromise.rejected;
      // 同理,reject時就去運(yùn)行我們存儲起來的對應(yīng)onRejected方法,注意要把參數(shù)傳遞給fn
      this.onRejectedFoo.forEach(fn => fn(err)); 
    }
  };

  then = (onResolved, onRejected) => {
  // 如果是pending狀態(tài),把成功的方法通過數(shù)組存起來
    if (this.status === 'pending' && onResolved) {
      this.onFulfilledFoo.push(onResolved);
    }
  // 如果是pending狀態(tài),把失敗的方法通過數(shù)組存起來
    if (this.status === 'pending' && onRejected) {  
      this.onRejectedFoo.push(onRejected);
    }
    if (this.status === 'fulfilled' && onResolved) {
      onResolved(this.susecessValue);
    }
    if (this.status === 'rejected' && onRejected) {
      onRejected(this.failedValue);
    }
  };
}

然后再來測試下我們剛才遇到的 setTimeout問題:

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('測試');
  }, 3000);
}).then(res => {
  console.log('fulfillment', res);   // 測試
}, err => {
  console.log('rejection', err);
});

很完美,3秒后打印出‘測試’兩個字,這就是我們要的結(jié)果。但是此時還有一個大問題,就是我們還不能支持鏈?zhǔn)秸{(diào)用。比如說以下這種情況:

function foo() {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('返回結(jié)果');
    }, 3000);
  });
}

foo().then(res => {
  console.log('1', res);
  return res;
}).then(res => res);  //這里的then會報錯:Cannot read properties of undefined (reading 'then')

我們想到是否可以直接在我們寫的promise中then方法直接return一個this,是可以的。但是解決不了問題。因?yàn)橛龅揭韵逻@個情況就涼涼了:

function foo() {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('測試');
    }, 3000);
  });
}

foo().then(res => {
  return res + '1111';
}).then(res => console.log(res));  // 測試
// 正確結(jié)果應(yīng)該是:測試1111

很顯然這種結(jié)果不是我們想要的,因?yàn)槲覀兊膖hen執(zhí)行完后沒有把后續(xù)的then繼續(xù)把方法放到promise里面執(zhí)行(理解為現(xiàn)在的結(jié)果是在promise外部執(zhí)行的)。所以我們需要在任何時候都返回一個promise,以供then鏈?zhǔn)秸{(diào)用。

4、增加支持鏈?zhǔn)秸{(diào)用(重點(diǎn))

我們需要先改造一下剛剛的寫法,1、then永遠(yuǎn)返回類實(shí)例本身(只有這樣才可以為無限.then().then()調(diào)用);如果是第一次調(diào)用then(即狀態(tài)為pending,此時promise內(nèi)部還沒給出結(jié)果) 就把then方法用來做存儲接收到的方法操作,即外部傳進(jìn)來來的方法都是用then存儲。 否則就直接執(zhí)行_handle方法(即異步完成了,后面的.then可以直接執(zhí)行),2、resolve、reject負(fù)傳遞剛剛通過then存儲的傳進(jìn)來的方法給_handle,3(重點(diǎn))、_handle負(fù)責(zé)判斷成功或者失敗情況下該執(zhí)行哪個對應(yīng)的方法。并且多了一步把then傳進(jìn)來的方法里面添加計(jì)算好的返回值。

class MyPromise {

  static pending = 'pending';
  static fulfilled = 'fulfilled';
  static rejected = 'rejected';

  constructor(executor) {
    this.saveFoo = []; // 只需要一個就可以存儲
    this.status = MyPromise.pending;
    this.susecessValue = undefined;
    this.failedValue = undefined;
    executor(this.resolve, this.reject);
  }

  resolve = (res) => {
    if (this.status === 'pending') {
      this.susecessValue = res;
      this.status = MyPromise.fulfilled;
      // 此時調(diào)用我們的handle去觸發(fā)外部傳進(jìn)來的方法
      this.saveFoo.forEach(fn => this._handle(fn));  
    }
  };

  reject = (err) => {
    if (this.status === 'pending') {
      this.failedValue = err;
      this.status = MyPromise.rejected;
      this.saveFoo.forEach(fn => this._handle(fn));  //同理
    }
  };

  /**
   * 如果是pending狀態(tài)(即第一次調(diào)用then,promise內(nèi)部還沒給出結(jié)果)
   * 就把then方法用來做存儲接收到的方法操作,即外部傳進(jìn)來來的方法都是用then存儲
   * 否則就直接執(zhí)行_handle方法(即異步完成了,后面的.then可以直接執(zhí)行)
   * @param onResolved
   * @param onRejected
   */
  then = (onResolved, onRejected) => {
    return new MyPromise((nextResolve, nexReject) => {
      if (this.status === 'pending') {
        this.saveFoo.push({onResolved, onRejected, nextResolve, nexReject});
      } else {
        this._handle({onResolved, onRejected, nextResolve, nexReject});
      }
    });
  };

  /**
   * 負(fù)責(zé)執(zhí)行成功或者失敗后的方法
   * @param callbacks
   * @returns {MyPromise}
   * @private
   */
  _handle = (callbacks) => {
    const { onResolved, onRejected, nextResolve, nexReject } = callbacks;

    if (this.status === 'fulfilled' && onResolved) {
      // 判斷是否有方法,有的話和我們最開始的一樣傳入值,否則返回默認(rèn)值
      const thenValue = onResolved ? onResolved(this.susecessValue) : this.susecessValue;
      // 把最開始得到的值繼續(xù)傳遞給下一個then中傳遞進(jìn)來的方法
      nextResolve(thenValue);
    }
    // 同理
    if (this.status === 'rejected' && onRejected) {
      const thenErr = onRejected ? onRejected(this.failedValue) : this.failedValue;
      nexReject(thenErr);
    }
  };
}

再次測試一下:

function foo() {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('測試');
    }, 3000);
  });
}

foo().then(res => {
  console.log('結(jié)果:', res);
  return res + '1111';
}).then(res => console.log('結(jié)果:', res));
// 結(jié)果: 測試
// 結(jié)果: 測試1111

到這里,我們算是解決了then示例調(diào)用的難題,還有一種情況,就是當(dāng)我們的第二個then里面resolve的是一個Promise而不是一個值的時候,比如:

function foo() {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('測試');
    }, 3000);
  });
}

foo().then(res => { 
  return new MyPromise((resolve, reject) => { // 如果這里再返回一個promise,我們就需要判斷
    resolve('測試222');
  });
}).then(res => console.log('結(jié)果:', res));

我們就需要對它判斷一下,如果是一個promise實(shí)例就直接去resolve或者reject返回結(jié)果,于是代碼里面加個promiseToThen方法判斷是否是實(shí)例本身,是的話就去走then方法返回結(jié)果。

class MyPromise {

  static pending = 'pending';
  static fulfilled = 'fulfilled';
  static rejected = 'rejected';

  constructor(executor) {
    this.saveFoo = []; // 只需要一個就可以存儲
    this.status = MyPromise.pending;
    this.susecessValue = undefined;
    this.failedValue = undefined;
    executor(this.resolve, this.reject);
  }

  resolve = (res) => {
    this.promiseToThen(res); // 判斷是否是一個promise
    if (this.status === 'pending') {
      this.susecessValue = res;
      this.status = MyPromise.fulfilled;
      this.saveFoo.forEach(fn => this._handle(fn)); 
    }
  };

  reject = (err) => {
    this.promiseToThen(err); // 判斷是否是一個promise
    if (this.status === 'pending') {
      this.failedValue = err;
      this.status = MyPromise.rejected;
      this.saveFoo.forEach(fn => this._handle(fn));  //同理
    }
  };

  /**
   * 如果是一個promise的本身實(shí)例就走then
   * @param Params
   */
  promiseToThen = (Params) => {
    if (Params instanceof MyPromise) {
      Params.then(this.resolve, this.reject);
    }
  };

  /**
   * 如果是pending狀態(tài)(即第一次調(diào)用then,promise內(nèi)部還沒給出結(jié)果)
   * 就把then方法用來做存儲接收到的方法操作,即外部傳進(jìn)來來的方法都是用then存儲
   * 否則就直接執(zhí)行_handle方法(即異步完成了,后面的.then可以直接執(zhí)行)
   * @param onResolved
   * @param onRejected
   */
  then = (onResolved, onRejected) => {
    return new MyPromise((nextResolve, nexReject) => {
      if (this.status === 'pending') {
        this.saveFoo.push({onResolved, onRejected, nextResolve, nexReject});
      } else {
        this._handle({onResolved, onRejected, nextResolve, nexReject});
      }
    });
  };

  /**
   * 負(fù)責(zé)執(zhí)行成功或者失敗后的方法
   * @param callbacks
   * @returns {MyPromise}
   * @private
   */
  _handle = (callbacks) => {
    const { onResolved, onRejected, nextResolve, nexReject } = callbacks;

    if (this.status === 'fulfilled' && onResolved) {
      // 判斷是否有方法,有的話和我們最開始的一樣傳入值,否則返回默認(rèn)值
      const thenValue = onResolved ? onResolved(this.susecessValue) : this.susecessValue;
      // 把最開始得到的值繼續(xù)傳遞給下一個then中傳遞進(jìn)來的方法
      nextResolve(thenValue);
    }
    // 同理
    if (this.status === 'rejected' && onRejected) {
      const thenErr = onRejected ? onRejected(this.failedValue) : this.failedValue;
      nexReject(thenErr);
    }
  };
}

最后,我們還有一個步驟沒做,就是promise規(guī)定,如果then的參數(shù)不是一個函數(shù)而是一個具體的值,我們就需要忽略并且返回對應(yīng)的結(jié)果;如果then()參數(shù)為空那么這個then可以不執(zhí)行,直接返回對應(yīng)結(jié)果就可以。比如

.then(res=>'下一個then返回一個具體數(shù)值').then(1).then(res=>console.log(res))
// 或者是這種情況
.then(res=>'下一個then參數(shù)為空').then().then(res=>console.log(res))

我們只需要在_handle方法處加上判斷即可,最終代碼如下:

class MyPromise {

  static pending = 'pending';
  static fulfilled = 'fulfilled';
  static rejected = 'rejected';

  constructor(executor) {
    this.saveFoo = []; // 只需要一個就可以存儲
    this.status = MyPromise.pending;
    this.susecessValue = undefined;
    this.failedValue = undefined;
    try {
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e)
    }
  }

  resolve = (res) => {
    this.promiseToThen(res); // 判斷是否是一個promise
    if (this.status === 'pending') {
      this.susecessValue = res;
      this.status = MyPromise.fulfilled;
      this.saveFoo.forEach(fn => this._handle(fn)); 
    }
  };

  reject = (err) => {
    this.promiseToThen(err); // 判斷是否是一個promise
    if (this.status === 'pending') {
      this.failedValue = err;
      this.status = MyPromise.rejected;
      this.saveFoo.forEach(fn => this._handle(fn));  //同理
    }
  };

  /**
   * 如果是一個promise的本身實(shí)例就走then
   * @param Params
   */
  promiseToThen = (Params) => {
    if (Params instanceof MyPromise) {
      Params.then(this.resolve, this.reject);
    }
  };


  /**
   * 把then方法用來做存儲接收到的方法操作,即外部傳進(jìn)來來的方法都是用then存儲
   * @param onResolved
   * @param onRejected
   */
  then = (onResolved, onRejected) => {
    return new MyPromise((nextResolve, nexReject) => {
      if (this.status === 'pending') {
        this.saveFoo.push({ onResolved, onRejected, nextResolve, nexReject });
      }
      this._handle({ onResolved, onRejected, nextResolve, nexReject });
    });
  };

  _handle = (callbacks) => {
    const {onResolved, onRejected, nextResolve, nexReject} = callbacks;
    if (typeof onResolved === 'undefined') { // 直接執(zhí)行下一個then方法中的函數(shù)
      typeof nextResolve === 'function' && nextResolve(this.susecessValue)
    }

  /**
   * 負(fù)責(zé)執(zhí)行成功或者失敗后的方法
   * @param callbacks
   * @returns {MyPromise}
   * @private
   */
    if (this.status === 'fulfilled' && onResolved) {
      // 如果參數(shù)不是一個函數(shù)
      const translateFun = typeof onResolved === 'function' ? onResolved : () => onResolved;
      // 判斷是否有方法,有的話和我們最開始的一樣傳入值,否則返回默認(rèn)值
      const thenValue = onResolved ? translateFun(this.susecessValue) : this.susecessValue;
      // 把最開始得到的值繼續(xù)傳遞給下一個then中傳遞進(jìn)來的方法
      nextResolve(thenValue);
    }
    // 同理
    if (this.status === 'rejected' && onRejected) {
      const translateFun = typeof onRejected === 'function' ? onRejected : () => onRejected;
      const thenErr = onRejected ? translateFun(this.failedValue) : this.failedValue;
      nexReject(thenErr);
    }
  };
}

最后:提示:任何手寫的promise無法達(dá)到與原生的promise的運(yùn)行順序一致的效果(但是我們能保證then是在異步回調(diào)后出發(fā),這其實(shí)是promise的一個重要點(diǎn)),因?yàn)樵膒romise里.then方法會被歸類到微任務(wù)那里,導(dǎo)致最終運(yùn)行順序不一致類似這種。

new Promise(resolve=>resolve(1)).then(res=>console.log(1))
console.log(2)
// 2  1

new MyPromise(resolve=>resolve(1)).then(res=>console.log(1))
console.log(2)
// 1  2
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容