溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么寫出符合Promise/A+ 規(guī)范Promise的源碼

發(fā)布時間:2021-10-29 15:53:23 來源:億速云 閱讀:131 作者:iii 欄目:web開發(fā)

這篇文章主要介紹“怎么寫出符合Promise/A+ 規(guī)范Promise的源碼”,在日常操作中,相信很多人在怎么寫出符合Promise/A+ 規(guī)范Promise的源碼問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么寫出符合Promise/A+ 規(guī)范Promise的源碼”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

Promise的源碼實現(xiàn)

/**   * 1. new Promise時,需要傳遞一個 executor 執(zhí)行器,執(zhí)行器立刻執(zhí)行   * 2. executor 接受兩個參數(shù),分別是 resolve 和 reject  * 3. promise 只能從 pending 到 rejected, 或者從 pending 到 fulfilled   * 4. promise 的狀態(tài)一旦確認,就不會再改變   * 5. promise 都有 then 方法,then 接收兩個參數(shù),分別是 promise 成功的回調(diào) onFulfilled,    *      和 promise 失敗的回調(diào) onRejected   * 6. 如果調(diào)用 then 時,promise已經(jīng)成功,則執(zhí)行 onFulfilled,并將promise的值作為參數(shù)傳遞進去。   *      如果promise已經(jīng)失敗,那么執(zhí)行 onRejected, 并將 promise 失敗的原因作為參數(shù)傳遞進去。   *      如果promise的狀態(tài)是pending,需要將onFulfilled和onRejected函數(shù)存放起來,等待狀態(tài)確定后,再依次將對應(yīng)的函數(shù)執(zhí)行(發(fā)布訂閱)   * 7. then 的參數(shù) onFulfilled 和 onRejected 可以缺省   * 8. promise 可以then多次,promise 的then 方法返回一個 promise   * 9. 如果 then 返回的是一個結(jié)果,那么就會把這個結(jié)果作為參數(shù),傳遞給下一個then的成功的回調(diào)(onFulfilled)   * 10. 如果 then 中拋出了異常,那么就會把這個異常作為參數(shù),傳遞給下一個then的失敗的回調(diào)(onRejected)   * 11.如果 then 返回的是一個promise,那么會等這個promise執(zhí)行完,promise如果成功,   *   就走下一個then的成功,如果失敗,就走下一個then的失敗   */  const PENDING = 'pending';  const FULFILLED = 'fulfilled';  const REJECTED = 'rejected';  function Promise(executor) {      let self = this;      self.status = PENDING;      self.onFulfilled = [];//成功的回調(diào)      self.onRejected = []; //失敗的回調(diào)      //PromiseA+ 2.1      function resolve(value) {          if (self.status === PENDING) {              self.status = FULFILLED;              self.value = value;              self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1          }      }      function reject(reason) {          if (self.status === PENDING) {              self.status = REJECTED;              self.reason = reason;              self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2          }      }     try {          executor(resolve, reject);      } catch (e) {          reject(e);      }  }  Promise.prototype.then = function (onFulfilled, onRejected) {      //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;      onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };      let self = this;      //PromiseA+ 2.2.7      let promise2 = new Promise((resolve, reject) => {          if (self.status === FULFILLED) {              //PromiseA+ 2.2.2              //PromiseA+ 2.2.4 --- setTimeout              setTimeout(() => {                  try {                      //PromiseA+ 2.2.7.1                      let x = onFulfilled(self.value);                      resolvePromise(promise2, x, resolve, reject);                  } catch (e) {                      //PromiseA+ 2.2.7.2                      reject(e);                  }              });          } else if (self.status === REJECTED) {              //PromiseA+ 2.2.3              setTimeout(() => {                  try {                      let x = onRejected(self.reason);                      resolvePromise(promise2, x, resolve, reject);                  } catch (e) {                      reject(e);                  }              });          } else if (self.status === PENDING) {              self.onFulfilled.push(() => {                  setTimeout(() => {                      try {                          let x = onFulfilled(self.value);                          resolvePromise(promise2, x, resolve, reject);                      } catch (e) {                          reject(e);                      }                  });              });              self.onRejected.push(() => {                  setTimeout(() => {                      try {                          let x = onRejected(self.reason);                          resolvePromise(promise2, x, resolve, reject);                      } catch (e) {                          reject(e);                      }                  });              });          }      });      return promise2;  }  function resolvePromise(promise2, x, resolve, reject) {      let self = this;      //PromiseA+ 2.3.1      if (promise2 === x) {          reject(new TypeError('Chaining cycle'));      }      if (x && typeof x === 'object' || typeof x === 'function') {          let used; //PromiseA+2.3.3.3.3 只能調(diào)用一次          try {              let then = x.then;              if (typeof then === 'function') {                  //PromiseA+2.3.3                  then.call(x, (y) => {                      //PromiseA+2.3.3.1                      if (used) return;                      used = true;                      resolvePromise(promise2, y, resolve, reject);                  }, (r) => {                      //PromiseA+2.3.3.2                      if (used) return;                      used = true;                      reject(r);                  });              }else{                  //PromiseA+2.3.3.4                  if (used) return;                  used = true;                  resolve(x);              }          } catch (e) {              //PromiseA+ 2.3.3.2              if (used) return;              used = true;              reject(e);          }      } else {          //PromiseA+ 2.3.3.4          resolve(x);      }  }  module.exports = Promise;

有專門的測試腳本可以測試所編寫的代碼是否符合PromiseA+的規(guī)范。

首先,在promise實現(xiàn)的代碼中,增加以下代碼:

PromisePromise.defer = Promise.deferred = function () {      let dfd = {};      dfd.promise = new Promise((resolve, reject) => {          dfd.resolve = resolve;          dfd.reject = reject;      });      return dfd;  }

安裝測試腳本:

npm install -g promises-aplus-tests

如果當(dāng)前的promise源碼的文件名為promise.js

那么在對應(yīng)的目錄執(zhí)行以下命令:

promises-aplus-tests promise.js

promises-aplus-tests中共有872條測試用例。以上代碼,可以完美通過所有用例。

對上面的代碼實現(xiàn)做一點簡要說明(其它一些內(nèi)容注釋中已經(jīng)寫得很清楚):

  1.  onFulfilled 和 onFulfilled的調(diào)用需要放在setTimeout,因為規(guī)范中表示: onFulfilled or onRejected must not be called until the execution context stack contains only platform code。使用setTimeout只是模擬異步,原生Promise并非是這樣實現(xiàn)的。

    2.  在 resolvePromise 的函數(shù)中,為何需要usedd這個flag,同樣是因為規(guī)范中明確表示: If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. 因此我們需要這樣的flag來確保只會執(zhí)行一次。

    3.  self.onFulfilled 和 self.onRejected 中存儲了成功的回調(diào)和失敗的回調(diào),根據(jù)規(guī)范2.6顯示,當(dāng)promise從pending態(tài)改變的時候,需要按照順序去指定then對應(yīng)的回調(diào)。

PromiseA+的規(guī)范(翻譯版)

PS: 下面是我翻譯的規(guī)范,供參考

術(shù)語

  1.   promise 是一個有then方法的對象或者是函數(shù),行為遵循本規(guī)范

  2.   thenable 是一個有then方法的對象或者是函數(shù)

  3.   value 是promise狀態(tài)成功時的值,包括 undefined/thenable或者是 promise

  4.   exception 是一個使用throw拋出的異常值

  5.   reason 是promise狀態(tài)失敗時的值

要求

2.1 Promise States

Promise 必須處于以下三個狀態(tài)之一: pending, fulfilled 或者是 rejected

2.1.1 如果promise在pending狀態(tài)

2.1.1.1 可以變成 fulfilled 或者是 rejected

2.1.2 如果promise在fulfilled狀態(tài)

2.1.2.1 不會變成其它狀態(tài)  2.1.2.2 必須有一個value值

2.1.3 如果promise在rejected狀態(tài)

2.1.3.1 不會變成其它狀態(tài)  2.1.3.2 必須有一個promise被reject的reason

概括即是:promise的狀態(tài)只能從pending變成fulfilled,或者從pending變成rejected.promise成功,有成功的value.promise失敗的話,有失敗的原因

2.2 then方法

promise必須提供一個then方法,來訪問最終的結(jié)果

promise的then方法接收兩個參數(shù)

promise.then(onFulfilled, onRejected)

2.2.1 onFulfilled 和 onRejected 都是可選參數(shù)

2.2.1.1 onFulfilled 必須是函數(shù)類型  2.2.1.2 onRejected 必須是函數(shù)類型

2.2.2 如果 onFulfilled 是函數(shù):

2.2.2.1 必須在promise變成 fulfilled 時,調(diào)用 onFulfilled,參數(shù)是promise的value  2.2.2.2 在promise的狀態(tài)不是 fulfilled 之前,不能調(diào)用  2.2.2.3 onFulfilled 只能被調(diào)用一次

2.2.3 如果 onRejected 是函數(shù):

2.2.3.1 必須在promise變成 rejected 時,調(diào)用 onRejected,參數(shù)是promise的reason  2.2.3.2 在promise的狀態(tài)不是 rejected 之前,不能調(diào)用  2.2.3.3 onRejected 只能被調(diào)用一次

2.2.4 onFulfilled 和 onRejected 應(yīng)該是微任務(wù)

2.2.5 onFulfilled  和 onRejected 必須作為函數(shù)被調(diào)用

2.2.6 then方法可能被多次調(diào)用

2.2.6.1 如果promise變成了 fulfilled態(tài),所有的onFulfilled回調(diào)都需要按照then的順序執(zhí)行  2.2.6.2 如果promise變成了 rejected態(tài),所有的onRejected回調(diào)都需要按照then的順序執(zhí)行

2.2.7 then必須返回一個promise

promise2 = promise1.then(onFulfilled, onRejected);  2.2.7.1 onFulfilled 或 onRejected 執(zhí)行的結(jié)果為x,調(diào)用 resolvePromise  2.2.7.2 如果 onFulfilled 或者 onRejected 執(zhí)行時拋出異常e,promise2需要被reject  2.2.7.3 如果 onFulfilled 不是一個函數(shù),promise2 以promise1的值fulfilled  2.2.7.4 如果 onRejected 不是一個函數(shù),promise2 以promise1的reason rejected

2.3 resolvePromise

resolvePromise(promise2, x, resolve, reject)

2.3.1 如果 promise2 和 x 相等,那么 reject promise with a TypeError

2.3.2 如果 x 是一個 promsie

2.3.2.1 如果x是pending態(tài),那么promise必須要在pending,直到 x 變成 fulfilled or rejected.  2.3.2.2 如果 x 被 fulfilled, fulfill promise with the same value.  2.3.2.3 如果 x 被 rejected, reject promise with the same reason.

2.3.3 如果 x 是一個 object 或者 是一個 function

2.3.3.1 let then = x.then.  2.3.3.2 如果 x.then 這步出錯,那么 reject promise with e as the reason..  2.3.3.3 如果 then 是一個函數(shù),then.call(x, resolvePromiseFn, rejectPromise)      2.3.3.3.1 resolvePromiseFn 的 入?yún)⑹?nbsp;y, 執(zhí)行 resolvePromise(promise2, y, resolve, reject);      2.3.3.3.2 rejectPromise 的 入?yún)⑹?nbsp;r, reject promise with r.      2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都調(diào)用了,那么第一個調(diào)用優(yōu)先,后面的調(diào)用忽略。      2.3.3.3.4 如果調(diào)用then拋出異常e           2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用,那么忽略          2.3.3.3.4.3 否則,reject promise with e as the reason  2.3.3.4 如果 then 不是一個function. fulfill promise with x.

2.3.4 如果 x 不是一個 object 或者 function,fulfill promise with x.

Promise的其他方法

雖然上述的promise源碼已經(jīng)符合PromiseA+的規(guī)范,但是原生的Promise還提供了一些其他方法,如:

  1.  Promise.resolve()

  2.  Promise.reject()

  3.  Promise.prototype.catch()

  4.  Promise.prototype.finally()

  5.  Promise.all()

  6.  Promise.race()

下面具體說一下每個方法的實現(xiàn):

Promise.resolve

Promise.resolve(value) 返回一個以給定值解析后的Promise 對象.

  1.  如果 value 是個 thenable 對象,返回的promise會“跟隨”這個thenable的對象,采用它的最終狀態(tài)

  2.  如果傳入的value本身就是promise對象,那么Promise.resolve將不做任何修改、原封不動地返回這個promise對象。

  3.  其他情況,直接返回以該值為成功狀態(tài)的promise對象。 

Promise.resolve = function (param) {          if (param instanceof Promise) {          return param;      }      return new Promise((resolve, reject) => {          if (param && typeof param === 'object' && typeof param.then === 'function') {              setTimeout(() => {                  param.then(resolve, reject);              });          } else {              resolve(param);          }      });  }

thenable對象的執(zhí)行加 setTimeout的原因是根據(jù)原生Promise對象執(zhí)行的結(jié)果推斷的,如下的測試代碼,原生的執(zhí)行結(jié)果為: 20  400  30;為了同樣的執(zhí)行順序,增加了setTimeout延時。

測試代碼:

let p = Promise.resolve(20);  p.then((data) => {      console.log(data);  });  let p2 = Promise.resolve({      then: function(resolve, reject) {          resolve(30);      }  });  p2.then((data)=> {      console.log(data)  });  let p3 = Promise.resolve(new Promise((resolve, reject) => {      resolve(400)  }));  p3.then((data) => {      console.log(data)  });

Promise.reject

Promise.reject方法和Promise.resolve不同,Promise.reject()方法的參數(shù),會原封不動地作為reject的理由,變成后續(xù)方法的參數(shù)。

Promise.reject = function (reason) {      return new Promise((resolve, reject) => {          reject(reason);      });  }

Promise.prototype.catch

Promise.prototype.catch 用于指定出錯時的回調(diào),是特殊的then方法,catch之后,可以繼續(xù) .then

Promise.prototype.catch = function (onRejected) {      return this.then(null, onRejected);  }

Promise.prototype.finally

不管成功還是失敗,都會走到finally中,并且finally之后,還可以繼續(xù)then。并且會將值原封不動的傳遞給后面的then.

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

Promise.all

Promise.all(promises) 返回一個promise對象

  1.  如果傳入的參數(shù)是一個空的可迭代對象,那么此promise對象回調(diào)完成(resolve),只有此情況,是同步執(zhí)行的,其它都是異步返回的。

  2.  如果傳入的參數(shù)不包含任何 promise,則返回一個異步完成.

  3.  promises 中所有的promise都promise都“完成”時或參數(shù)中不包含 promise 時回調(diào)完成。

  4.  如果參數(shù)中有一個promise失敗,那么Promise.all返回的promise對象失敗

  5.  在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結(jié)果都是一個數(shù)組 

Promise.all = function (promises) {      promises = Array.from(promises);//將可迭代對象轉(zhuǎn)換為數(shù)組      return new Promise((resolve, reject) => {          let index = 0;          let result = [];          if (promises.length === 0) {              resolve(result);          } else {              function processValue(i, data) {                  result[i] = data;                  if (++index === promises.length) {                      resolve(result);                  }              }              for (let i = 0; i < promises.length; i++) {                    //promises[i] 可能是普通值                    Promise.resolve(promises[i]).then((data) => {                      processValue(i, data);                  }, (err) => {                      reject(err);                      return;                  });              }          }      });  }

測試代碼:

var promise1 = new Promise((resolve, reject) => {      resolve(3);  })  var promise2 = 42;  var promise3 = new Promise(function(resolve, reject) {    setTimeout(resolve, 100, 'foo');  });  Promise.all([promise1, promise2, promise3]).then(function(values) {    console.log(values); //[3, 42, 'foo']  },(err)=>{      console.log(err)  });  var p = Promise.all([]); // will be immediately resolved  var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously  console.log(p);  console.log(p2)  setTimeout(function(){      console.log('the stack is now empty');      console.log(p2);  });

Promise.race

Promise.race函數(shù)返回一個 Promise,它將與第一個傳遞的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失?。╮ejects),這要取決于第一個完成的方式是兩個中的哪個。

如果傳的參數(shù)數(shù)組是空,則返回的 promise 將永遠等待。

如果迭代包含一個或多個非承諾值和/或已解決/拒絕的承諾,則 Promise.race 將解析為迭代中找到的第一個值。

Promise.race = function (promises) {      promises = Array.from(promises);//將可迭代對象轉(zhuǎn)換為數(shù)組      return new Promise((resolve, reject) => {          if (promises.length === 0) {              return;          } else {              for (let i = 0; i < promises.length; i++) {                  Promise.resolve(promises[i]).then((data) => {                      resolve(data);                      return;                  }, (err) => {                      reject(err);                      return;                  });              }          }      });  }

測試代碼:

Promise.race([      new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),      undefined,      new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })  ]).then((data) => {      console.log('success ', data);  }, (err) => {      console.log('err ',err);  });  Promise.race([      new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),      new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),      new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })  ]).then((data) => {      console.log(data);  }, (err) => {      console.log(err);  });

到此,關(guān)于“怎么寫出符合Promise/A+ 規(guī)范Promise的源碼”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI