文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何理解Promise

2024-04-02 19:55

关注

这篇文章主要介绍“如何理解Promise”,在日常操作中,相信很多人在如何理解Promise问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解Promise”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

大概的架子

通过我们经常写的 promise 语法,我们可以先写一个大概的架子出来,promise 接受回调,并且调用,自身带有三种状态,pendding, onFulfilled, onRejected,并且 resolve 这个函数可以让 pendding 状态变成 onFulfilled 状态,同理 reject 函数可以让 pendding 状态变成 onRejected 状态。我们先把上面描述部分实现了。

const PromiseCopy = function (fn) {    this.info = {      status: "pending",      value: "",    };    const self = this;    self.onFulfilledArr = []; // then函数里面的第一个回调函数的集合    self.onRejectedArr = []; // then函数里面的第二个回调函数的集合    const resolve = function (value) {      // 加这个判断是为了表示,只有在pendding状态下才会去执行      // 状态已经变成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 的附加实现

其实写到这里我们的 resolve 函数还是有一些功能没有实现的, 我们知道 调用 resolve(x), x 的值有好几种情况,如下

思考

const PromiseCopy = function (fn) {    this.info = {      status: "pending",      value: "",    };    const self = this;    self.onFulfilledArr = []; // then函数里面的第一个回调函数的集合    self.onRejectedArr = []; // then函数里面的第二个回调函数的集合    // _resolve 是我们经常调用的resolve    // 但是真正实现的应该是里面的resolve    const _resolve = function (value) {      // 这个函数得改变一下      // PromiseCopy一旦被实例化,那么self就是实例本身了      resolve(self, value);    };    // 此时我们就可以在resolve进行判断了    const resolve = function (promise, value) {      let ifexec = false;      // 首先判断value是不是promise本身      if (value === promise) {        // 一定要用TypeError写 不然promises-aplus-tests跑不通        // 切记这是第一个坑,promises-aplus-tests只认TypeError这种错误形式        reject(new TypeError("A promise cannot be onFulfilled with itself."));      }      // value是一个thenable对象      // 这个要Object.prototype.toString.call(value) === "[object Object]"判断      // 不然resolve([])有问题,不知道是不是我实现问题      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会一直报错,这是第三个大坑        // 因为promises-aplus-test测试里面有这一条的        // 看上面注释例子        try {          // 拿到then函数          const then = value.then;          // 如果then是一个函数则执行这个函数          if (typeof then === "function") {            // 为什么要.call(value, x, y) 你们可以自己试一下原生的Promise在这种情况下this指向的就是value,所以要绑定            // 因为then我们已经拿出来了then = value.then,直接调用then(),this就指向的window            // 为什么后面还需要绑定两个函数了            // 根据原生的Promise可知,thenable中的then函数可以接受两个函数resolve,reject            // 只有手动调用了resolve和reject才会执行后面的.then操作,具体大家自己操作下            then.call(              value,              function (value) {                if (ifexec) {                  return;                }                // ifexec这个一定要加,不然也会报200ms错误,第四个大坑                // 目的是为了不让多次执行,语言无法表达看下面的例子                // 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);        }     }      // 下面这一点非常的重要,是async,await 和一些插件比如saga的核心      // 就是如果x是一个promise对象,那么then的执行取决于x的状态      // 还有这一个判断一定要放在这里,不要和上面的换 不然promises-aplus-tests会报一个超过200ms的错误,切记这是第二个坑      if (value && value instanceof PromiseCopy && value.then === promise.then) {        // 将promise的onFulfilledArr给到value        // 但是还没有那么简单我们要明白两点        // 如果value这个promise已经不是pendding,我们给了他也没有用,所以需要直接调用        if (value.info.status === "pending") {          value.onFulfilledArr = self.onFulfilledArr;          value.onRejectedArr = self.onRejectedArr;        }        // 如果value状态是onFulfilled        if (value.info.status === "onRejected") {          self.info.value = value.info.value;          self.onRejectedArr.forEach((fn) => fn(value.info.value));       }        // 如果value状态是reject        if (value.info.status === "onFulfilled") {          self.info.value = value.info.value;          self.onFulfilledArr.forEach((fn) => fn(value.info.value));        }        return;      }      // 如果是一个普通的值      // 加这个判断是为了表示,只有在pendding状态下才会去执行      // 状态已经变成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那么多规则,比较简单    const reject = function (value) {      if (self.info.status === "pending") {        self.info.status = "onRejected";        self.info.value = value;        self.onRejectedArr.forEach((fn) => fn(value));      }    };    // 此时fn调用的是_reoslve    // 这个try catch主要是实现promiseCopy.prototype.catch    try {      fn(_resolve, reject);    } catch (e) {      setTimeout(() => {        self.onRejectedArr.forEach((fn) => fn(e));      });    }  };

then 的实现

我们上面介绍的是 promise 的 resolve 用法,promise 还有一个基本用法就是后面接 then,因为是.then 所以我们想到的是这个 then 方法挂在到原型上的,那么 new PromiseCopy 的时候就可以得到这个 then。then 里面是两个函数,一个是 onFulfilled 后执行的回调,一个是 onRejected 后执行的回调。现在的问题是他是怎么做到 then 里面的函数是在 resolve 和 reject 后执行的?这种推迟执行或者说在某种情况下去执行我们想到的就是观察者模式了。下面用代码把上面的话实现一遍,在代码里面会写详细一点的注释。

PromiseCopy.prototype.then = function (onFulfilled, onRejected) {    const self = this;    // 这里要判断下,如果PromiseCopy是resolve了那么就直接执行onFulfilled    if (self.info.status === "onFulfilled") {      setTimeout(() => {        onFulfilled(self.info.value);      });    }    if (self.info.status === "onRejected") {      setTimeout(() => {        onRejected(self.info.value);      });   }    // 根据PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必须是函数,不然就会被忽略    if (typeof onFulfilled === "function") {      self.onFulfilledArr.push(() => {        setTimeout(() => {          onFulfilled(self.info.value);        });      });    }    if (typeof onRejected === "function") {      self.onRejectedArr.push(() => {        setTimeout(() => {          onRejected(self.info.value);        });      });    }    // 根据PromiseA+ 2.2.7规范 then函数必须返回一个promise对象    return new PromiseCopy((resolve, reject) => {});  };

then 的额外实现

上面实现的 then 也是一个简单的用法,不过根据 PromiseA+的规范这个 then 函数还有几个点没有实现,看代码解释

promise2 = promise1.then(onFulfilled, onRejected);  promise2.then(onFulfilled, onRejected);

思考

如果像上面这么说的话,这个新抛出来的 promise 何时调用这个 resolve 或者 reject 是一个关键, 并且这个抛出的 promise 的执行还得看 onFulfilled 和 onRejected 返回值,这一点当时写 promise 的时候想了很久,不知道如何组织,后来实在想不出来,看了下网上很多文章,发现这些逻辑都是在 PromiseCopy 主体里面实现的。

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

then 实现加强版

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不是一个函数resolve--self.info.value            let value = self.info.value;            // 这个注释不要,留着只是为了记录当时的思路            // 这个加判断是为了防止then函数逇回调不是一个函数,,是一个字符串            //   if (typeof onFulfilled === "function") {            //     value = onFulfilled(value);            //   }            value = onFulfilled(value);            // 这里要做一个[[Resolve]](promise2, x)处理了            // 因为resolve里面直接做了,所以直接调用,和网上的一些实现有点不一样            // 他们是提取了一个resolvePromise函数调用,我是直接调用了resolve            resolve(value);          } catch (e) {            reject(e);          }        });      }      // 注意这里根据上面可知onFulfilled,onRejected抛出的值都要经过[[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状态也需要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;  };

小结

到这里 promise 的主体实现已经完成了,下面是测试结果

如何理解Promise

如何理解Promise

Promise 其他静态方法

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

这个方法有几个特点如下

PromiseCopy.all = function (data) {    let count = 0; // 记录调用次数    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

这个方法也有以下几个特点

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的    // 而且状态一定是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;  };  // 后来发现catch有一个简单的实现方法  // 没有删除上面就是为了记录思路过程  Promise.prototype.catch = function (onRejected) {    return this.then(null, onRejected);  };

deferred

这个是 Promise 提供的一个快捷使用,自己实现 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;  };

到此,关于“如何理解Promise”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯