溫馨提示×

溫馨提示×

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

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

如何理解Promise

發(fā)布時間:2021-10-26 10:06:57 來源:億速云 閱讀:133 作者:iii 欄目:web開發(fā)

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

大概的架子

通過我們經(jīng)常寫的 promise 語法,我們可以先寫一個大概的架子出來,promise 接受回調(diào),并且調(diào)用,自身帶有三種狀態(tài),pendding, onFulfilled, onRejected,并且 resolve 這個函數(shù)可以讓 pendding 狀態(tài)變成 onFulfilled 狀態(tài),同理 reject 函數(shù)可以讓 pendding 狀態(tài)變成 onRejected 狀態(tài)。我們先把上面描述部分實(shí)現(xiàn)了。

const PromiseCopy = function (fn) {    this.info = {      status: "pending",      value: "",    };    const self = this;    self.onFulfilledArr = []; // then函數(shù)里面的第一個回調(diào)函數(shù)的集合    self.onRejectedArr = []; // then函數(shù)里面的第二個回調(diào)函數(shù)的集合    const resolve = function (value) {      // 加這個判斷是為了表示,只有在pendding狀態(tài)下才會去執(zhí)行      // 狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了      // 符合PromiseA+中的2.1.2.1      if (self.info.status === "pending") {        self.info.status = "onFulfilled";        self.info.value = value;        self.onFulfilledArr.forEach((fn) => fn(value));      }    };    // 和上面同理符合PromiseA+,2.1.3.1    const reject = function (value) {      if (self.info.status === "pending") {        self.info.status = "onRejected";         self.info.value = value;        self.onRejectedArr.forEach((fn) => fn(value));      }    };    fn(resolve, reject);  };

resolve 的附加實(shí)現(xiàn)

其實(shí)寫到這里我們的 resolve 函數(shù)還是有一些功能沒有實(shí)現(xiàn)的, 我們知道 調(diào)用 resolve(x), x 的值有好幾種情況,如下

  •  如果 x 是 Promise 實(shí)例本身,則拋出錯誤

  •  如果 x 是一個 Promise 對象,那么 then 函數(shù)的執(zhí)行取決這個 x 的狀態(tài),如果 x 也調(diào)用 resolve(y),其中 y 也是一個 promise 對象.那么 then 函數(shù)的執(zhí)行取決于這個 promise 對象,依次類推,直到最后一個 promise 狀態(tài)更改

  •  如果 x 是一個 thenable 對象,就是一個對象包含 then 這個屬性,或者是一個函數(shù)包含一個 then 的靜態(tài)方法,那么直接執(zhí)行 then 函數(shù)

  •  如果 x 是一個普通值,直接變成 onFulfilled 狀態(tài),執(zhí)行后面的 then 函數(shù)

思考

  •  網(wǎng)上實(shí)現(xiàn)的大部分 resolve 都是我上面的代碼,但是根據(jù)規(guī)范,resolve 函數(shù)里面應(yīng)該是要判斷上面幾點(diǎn)的,所以我們上面寫的代碼是有誤的

  •  還有一個問題是,我們需要在 resolve 函數(shù)里面判斷 x 是不是實(shí)例的本身,但是正常的 resolve 函數(shù)我們經(jīng)常是傳入一個參數(shù),所以中間肯定是有一個中間函數(shù)的,看下面的代碼 

const PromiseCopy = function (fn) {    this.info = {      status: "pending",      value: "",    };    const self = this;    self.onFulfilledArr = []; // then函數(shù)里面的第一個回調(diào)函數(shù)的集合    self.onRejectedArr = []; // then函數(shù)里面的第二個回調(diào)函數(shù)的集合    // _resolve 是我們經(jīng)常調(diào)用的resolve    // 但是真正實(shí)現(xiàn)的應(yīng)該是里面的resolve    const _resolve = function (value) {      // 這個函數(shù)得改變一下      // PromiseCopy一旦被實(shí)例化,那么self就是實(shí)例本身了      resolve(self, value);    };    // 此時我們就可以在resolve進(jìn)行判斷了    const resolve = function (promise, value) {      let ifexec = false;      // 首先判斷value是不是promise本身      if (value === promise) {        // 一定要用TypeError寫 不然promises-aplus-tests跑不通        // 切記這是第一個坑,promises-aplus-tests只認(rèn)TypeError這種錯誤形式        reject(new TypeError("A promise cannot be onFulfilled with itself."));      }      // value是一個thenable對象      // 這個要Object.prototype.toString.call(value) === "[object Object]"判斷      // 不然resolve([])有問題,不知道是不是我實(shí)現(xiàn)問題      if (        value &&        (Object.prototype.toString.call(value) === "[object Object]" ||          typeof value === "function")      ) {        // var promise1 = Promise.resolve(dump).then(function () {        //   return {        //     then: (resolve, reject) => {        //       setTimeout(() => {        //         resolve({        //           then: (resolve, reject) => {        //             resolve("aa111a");        //             throw "other";        //           },        //         });        //       });        //     },        //   };        // });        // promise1.then(        //   (res) => {        //     console.log(res === "aa111a");        //     console.log("aaa");        //   },        //   (res) => {        //     console.log(res);        //     console.log("error");        //   }        // );        // 這里的try--catch一定要加 ,不然會promises-aplus-tests會一直報錯,這是第三個大坑        // 因?yàn)閜romises-aplus-test測試?yán)锩嬗羞@一條的        // 看上面注釋例子        try {          // 拿到then函數(shù)          const then = value.then;          // 如果then是一個函數(shù)則執(zhí)行這個函數(shù)          if (typeof then === "function") {            // 為什么要.call(value, x, y) 你們可以自己試一下原生的Promise在這種情況下this指向的就是value,所以要綁定            // 因?yàn)閠hen我們已經(jīng)拿出來了then = value.then,直接調(diào)用then(),this就指向的window            // 為什么后面還需要綁定兩個函數(shù)了            // 根據(jù)原生的Promise可知,thenable中的then函數(shù)可以接受兩個函數(shù)resolve,reject            // 只有手動調(diào)用了resolve和reject才會執(zhí)行后面的.then操作,具體大家自己操作下            then.call(              value,              function (value) {                if (ifexec) {                  return;                }                // ifexec這個一定要加,不然也會報200ms錯誤,第四個大坑                // 目的是為了不讓多次執(zhí)行,語言無法表達(dá)看下面的例子                // var promise1 = Promise.resolve(dump).then(function () {                //   return {                //     then: (resolve, reject) => {                //       resolve("aa111a");                //       resolve("aa111a");                //     },                //   };                // });                ifexec = true;                resolve(promise, value);              },              function (value) {                if (ifexec) {                  return;                }                ifexec = true;                reject(value);              }            );            return;          }        } catch (e) {          if (ifexec) {            return;          }          ifexec = true;          reject(e);        }     }      // 下面這一點(diǎn)非常的重要,是async,await 和一些插件比如saga的核心      // 就是如果x是一個promise對象,那么then的執(zhí)行取決于x的狀態(tài)      // 還有這一個判斷一定要放在這里,不要和上面的換 不然promises-aplus-tests會報一個超過200ms的錯誤,切記這是第二個坑      if (value && value instanceof PromiseCopy && value.then === promise.then) {        // 將promise的onFulfilledArr給到value        // 但是還沒有那么簡單我們要明白兩點(diǎn)        // 如果value這個promise已經(jīng)不是pendding,我們給了他也沒有用,所以需要直接調(diào)用        if (value.info.status === "pending") {          value.onFulfilledArr = self.onFulfilledArr;          value.onRejectedArr = self.onRejectedArr;        }        // 如果value狀態(tài)是onFulfilled        if (value.info.status === "onRejected") {          self.info.value = value.info.value;          self.onRejectedArr.forEach((fn) => fn(value.info.value));       }        // 如果value狀態(tài)是reject        if (value.info.status === "onFulfilled") {          self.info.value = value.info.value;          self.onFulfilledArr.forEach((fn) => fn(value.info.value));        }        return;      }      // 如果是一個普通的值      // 加這個判斷是為了表示,只有在pendding狀態(tài)下才會去執(zhí)行      // 狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了      // 符合PromiseA+中的2.1.2.1      if (self.info.status === "pending") {        self.info.status = "onFulfilled";        self.info.value = value;        self.onFulfilledArr.forEach((fn) => fn(value));      }    };    // 和上面同理符合PromiseA+,2.1.3.1    // reject沒有resolve那么多規(guī)則,比較簡單    const reject = function (value) {      if (self.info.status === "pending") {        self.info.status = "onRejected";        self.info.value = value;        self.onRejectedArr.forEach((fn) => fn(value));      }    };    // 此時fn調(diào)用的是_reoslve    // 這個try catch主要是實(shí)現(xiàn)promiseCopy.prototype.catch    try {      fn(_resolve, reject);    } catch (e) {      setTimeout(() => {        self.onRejectedArr.forEach((fn) => fn(e));      });    }  };

then 的實(shí)現(xiàn)

我們上面介紹的是 promise 的 resolve 用法,promise 還有一個基本用法就是后面接 then,因?yàn)槭?then 所以我們想到的是這個 then 方法掛在到原型上的,那么 new PromiseCopy 的時候就可以得到這個 then。then 里面是兩個函數(shù),一個是 onFulfilled 后執(zhí)行的回調(diào),一個是 onRejected 后執(zhí)行的回調(diào)?,F(xiàn)在的問題是他是怎么做到 then 里面的函數(shù)是在 resolve 和 reject 后執(zhí)行的?這種推遲執(zhí)行或者說在某種情況下去執(zhí)行我們想到的就是觀察者模式了。下面用代碼把上面的話實(shí)現(xiàn)一遍,在代碼里面會寫詳細(xì)一點(diǎn)的注釋。

PromiseCopy.prototype.then = function (onFulfilled, onRejected) {    const self = this;    // 這里要判斷下,如果PromiseCopy是resolve了那么就直接執(zhí)行onFulfilled    if (self.info.status === "onFulfilled") {      setTimeout(() => {        onFulfilled(self.info.value);      });    }    if (self.info.status === "onRejected") {      setTimeout(() => {        onRejected(self.info.value);      });   }    // 根據(jù)PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必須是函數(shù),不然就會被忽略    if (typeof onFulfilled === "function") {      self.onFulfilledArr.push(() => {        setTimeout(() => {          onFulfilled(self.info.value);        });      });    }    if (typeof onRejected === "function") {      self.onRejectedArr.push(() => {        setTimeout(() => {          onRejected(self.info.value);        });      });    }    // 根據(jù)PromiseA+ 2.2.7規(guī)范 then函數(shù)必須返回一個promise對象    return new PromiseCopy((resolve, reject) => {});  };

then 的額外實(shí)現(xiàn)

上面實(shí)現(xiàn)的 then 也是一個簡單的用法,不過根據(jù) PromiseA+的規(guī)范這個 then 函數(shù)還有幾個點(diǎn)沒有實(shí)現(xiàn),看代碼解釋

promise2 = promise1.then(onFulfilled, onRejected);  promise2.then(onFulfilled, onRejected);
  •  promise1.then 中的 onFulfilled,onRejected 函數(shù)如果返回一個 x,那么當(dāng)作[[Resolve]](promise2, x)來處理,就跟上面的 resolve 一樣處理,注意如果函數(shù)什么都沒有返回,就是返回的 undefined

  •  promise1.then 函數(shù)中的兩個回調(diào)函數(shù)只要有一個報錯,那么直接調(diào)用 promise2.then 函數(shù)中的錯誤回調(diào)

  •  如果 promise1.then 的第一個回調(diào)不是函數(shù),并且 promise1 調(diào)用的是 resolve,那么 promise2.then 的第一個回調(diào)參數(shù)是 promise1 中 resolve 函數(shù)的拋出值

  •  同理,如果 promise1.then 第二個回調(diào)不是函數(shù),并且 promise1 調(diào)用的是 reject,那么 promise2.then 中的錯誤回調(diào)就會執(zhí)行

思考

如果像上面這么說的話,這個新拋出來的 promise 何時調(diào)用這個 resolve 或者 reject 是一個關(guān)鍵, 并且這個拋出的 promise 的執(zhí)行還得看 onFulfilled 和 onRejected 返回值,這一點(diǎn)當(dāng)時寫 promise 的時候想了很久,不知道如何組織,后來實(shí)在想不出來,看了下網(wǎng)上很多文章,發(fā)現(xiàn)這些邏輯都是在 PromiseCopy 主體里面實(shí)現(xiàn)的。

return new PromiseCopy((resolve, reject) => {});

then 實(shí)現(xiàn)加強(qiáng)版

PromiseCopy.prototype.then = function (onFulfilled, onRejected) {    const self = this;    // 這個一定要這么寫目的為了讓值傳遞    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val;    // 這個一定要這么寫,一定要拋出一個錯throw err    onRejected =      typeof onRejected === "function"        ? onRejected        : (err) => {            throw err;          };    const newnewPromise = new PromiseCopy((resolve, reject) => {      if (self.info.status === "onFulfilled") {        setTimeout(() => {          try {            // 如果onFulfilled不是一個函數(shù)resolve--self.info.value            let value = self.info.value;            // 這個注釋不要,留著只是為了記錄當(dāng)時的思路            // 這個加判斷是為了防止then函數(shù)逇回調(diào)不是一個函數(shù),,是一個字符串            //   if (typeof onFulfilled === "function") {            //     value = onFulfilled(value);            //   }            value = onFulfilled(value);            // 這里要做一個[[Resolve]](promise2, x)處理了            // 因?yàn)閞esolve里面直接做了,所以直接調(diào)用,和網(wǎng)上的一些實(shí)現(xiàn)有點(diǎn)不一樣            // 他們是提取了一個resolvePromise函數(shù)調(diào)用,我是直接調(diào)用了resolve            resolve(value);          } catch (e) {            reject(e);          }        });      }      // 注意這里根據(jù)上面可知onFulfilled,onRejected拋出的值都要經(jīng)過[[Resolve]](promise2, x)      // 這和resolve,reject不一樣,promise中resolve才走[[Resolve]](promise2, x),reject不走      if (self.info.status === "onRejected") {        setTimeout(() => {          try {            let { value } = self.info;            value = onRejected(self.info.value);           resolve(value);          } catch (e) {            reject(e);          }        });      }      // 如果是pending狀態(tài)也需要push      if (self.info.status === "pending") {        self.onFulfilledArr.push((data) => {          setTimeout(() => {            try {              let value = data;              value = onFulfilled(value);              resolve(value);            } catch (e) {              reject(e);            }          });        });       self.onRejectedArr.push((data) => {          setTimeout(() => {            try {              let value = data;              value = onRejected(data);              resolve(value);            } catch (e) {              reject(e);            }          });        });      }    });    return newPromise;  };

小結(jié)

到這里 promise 的主體實(shí)現(xiàn)已經(jīng)完成了,下面是測試結(jié)果

如何理解Promise

如何理解Promise

Promise 其他靜態(tài)方法

Promise.resolve

PromiseCopy.resolve = function (data) {    return new PromiseCopy((resolve, reject) => {      resolve(data);    });  };

reject

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

Promise.all

這個方法有幾個特點(diǎn)如下

  •  該方法接受一個數(shù)組,數(shù)組每一個元素都是一個 promise 對象

  •  只有所有 promise 都是 onFulfilled 的時候才會執(zhí)行 then 回調(diào),并且結(jié)果順序和數(shù)組的一致

  •  如果其中一個 promise 發(fā)生了 reject 那么就會返回這個值 

PromiseCopy.all = function (data) {    let count = 0; // 記錄調(diào)用次數(shù)    let total = data.length;    let result = [];   return new PromiseCopy((resolve, reject) => {      for (let i = 0; i < total; i++) {        data[i].then(          (res) => {            result.push(res);            ++count;            if (count === totla) {              resolve(result);            }          },          (res) => {            return reject(res);          }        );      }    });  };

Promise.race

這個方法也有以下幾個特點(diǎn)

  •  這個方法也是接受數(shù)組,數(shù)組的元素是 promise

  •  他只返回最快的那一個 promise 的值

  •  就算有錯誤也會返回最快那一個 promise 的值 

PromiseCopy.race = function (data) {    const total = data.length;    return new PromiseCopy((resolve, reject) => {      for (let i = 0; i < total; i++) {        data[i].then(          (res) => {            resolve(res);          },          (res) => {            return reject(res);          }        );      }    });  };

catch 方法

PromiseCopy.prototype.catch = function (onRejected) {    // 能到catch里面來的一定是走的reject的    // 而且狀態(tài)一定是pendding    const self = this;    const newnewPromise = new PromiseCopy((resolve, reject) => {      if (self.info.status === "onRejected") {        try {          setTimeout(() => {            let { value } = self.info;            if (typeof onRejected === "function") {              value = onRejected(self.info.value);            }            resolve(value);          });        } catch (e) {          rejetc(e);        }     }      if (self.info.status === "pending") {        self.onRejectedArr.push((data) => {          setTimeout(() => {            try {              let value = data;              if (typeof onRejected === "function") {                value = onRejected(data);              }              resolve(value);            } catch (e) {              reject(e);            }          });        });      }    });    return newPromise;  };  // 后來發(fā)現(xiàn)catch有一個簡單的實(shí)現(xiàn)方法  // 沒有刪除上面就是為了記錄思路過程  Promise.prototype.catch = function (onRejected) {    return this.then(null, onRejected);  };

deferred

這個是 Promise 提供的一個快捷使用,自己實(shí)現(xiàn) promise 的時候一定要加,不然 promises-aplus-tests promise.js 跑不過

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

到此,關(guān)于“如何理解Promise”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

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

AI