广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >如何理解Promise
  • 943
分享到

如何理解Promise

2024-04-02 19:04:59 943人浏览 泡泡鱼
摘要

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

这篇文章主要介绍“如何理解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 的值有好几种情况,如下

  •  如果 x 是 Promise 实例本身,则抛出错误

  •  如果 x 是一个 Promise 对象,那么 then 函数的执行取决这个 x 的状态,如果 x 也调用 resolve(y),其中 y 也是一个 promise 对象.那么 then 函数的执行取决于这个 promise 对象,依次类推,直到最后一个 promise 状态更改

  •  如果 x 是一个 thenable 对象,就是一个对象包含 then 这个属性,或者是一个函数包含一个 then 的静态方法,那么直接执行 then 函数

  •  如果 x 是一个普通值,直接变成 onFulfilled 状态,执行后面的 then 函数

思考

  •  网上实现的大部分 resolve 都是我上面的代码,但是根据规范,resolve 函数里面应该是要判断上面几点的,所以我们上面写的代码是有误的

  •  还有一个问题是,我们需要在 resolve 函数里面判断 x 是不是实例的本身,但是正常的 resolve 函数我们经常是传入一个参数,所以中间肯定是有一个中间函数的,看下面的代码 

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);
  •  promise1.then 中的 onFulfilled,onRejected 函数如果返回一个 x,那么当作[[Resolve]](promise2, x)来处理,就跟上面的 resolve 一样处理,注意如果函数什么都没有返回,就是返回的 undefined

  •  promise1.then 函数中的两个回调函数只要有一个报错,那么直接调用 promise2.then 函数中的错误回调

  •  如果 promise1.then 的第一个回调不是函数,并且 promise1 调用的是 resolve,那么 promise2.then 的第一个回调参数是 promise1 中 resolve 函数的抛出值

  •  同理,如果 promise1.then 第二个回调不是函数,并且 promise1 调用的是 reject,那么 promise2.then 中的错误回调就会执行

思考

如果像上面这么说的话,这个新抛出来的 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

这个方法有几个特点如下

  •  该方法接受一个数组,数组每一个元素都是一个 promise 对象

  •  只有所有 promise 都是 onFulfilled 的时候才会执行 then 回调,并且结果顺序和数组的一致

  •  如果其中一个 promise 发生了 reject 那么就会返回这个值 

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

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

  •  这个方法也是接受数组,数组的元素是 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的    // 而且状态一定是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”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: 如何理解Promise

本文链接: https://www.lsjlt.com/news/84443.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何理解Promise
    这篇文章主要介绍“如何理解Promise”,在日常操作中,相信很多人在如何理解Promise问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解Promise”的疑惑有所帮...
    99+
    2022-10-19
  • 如何理解JavaScript中的Promise
    如何理解JavaScript中的Promise,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。pr...
    99+
    2022-10-19
  • 怎么理解ES6 Promise对象
    这篇文章主要讲解了“怎么理解ES6 Promise对象”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解ES6 Promise对象”吧!概述是异步编程的...
    99+
    2022-10-19
  • 怎么理解Promise中的core.js
    这篇文章主要讲解了“怎么理解Promise中的core.js”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解Promise中的core.js”吧!源码...
    99+
    2022-10-19
  • 图解 Promise 实现原理(四)—— Promise 静态方法实现
    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ 作者:Morrain了用法,原生提供了Promise对象。更多关于 Promi...
    99+
    2023-06-03
  • 一文详解如何有效的处理Promise并发
    目录前言Promise.all如何实现如何处理报错Promise.allSettled如何实现最后两个技巧Promise.racePromise.any总结前言 如上图所示的代码,...
    99+
    2023-05-16
    有效处理Promise并发 Promise并发
  • JavaScript Promise执行流程深刻理解
    目录手撕Promise看完收获Promise分析作用特点总体实现resolve和reject初步实现then方法的实现Promise.all的实现手撕Promise 手写一个Prom...
    99+
    2022-11-13
  • 如何掌握Promise
    本文小编为大家详细介绍“如何掌握Promise”,内容详细,步骤清晰,细节处理妥当,希望这篇“如何掌握Promise”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。异步编程想要学习promise,你必须要懂得什么是...
    99+
    2023-07-05
  • 图解 Promise 实现原理(一)—— 基础实现
    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ 作者:孔垂亮很多同学在学习 Promise 时,知其然却不知其所以然,对其中的...
    99+
    2023-06-03
  • Vue Promise如何解决回调地狱问题
    本篇内容介绍了“Vue Promise如何解决回调地狱问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!问题首先,什么是回调地狱:...
    99+
    2023-07-05
  • 如何解决vue-resource promise兼容性问题
    这篇文章主要为大家展示了“如何解决vue-resource promise兼容性问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决vue-resourc...
    99+
    2022-10-19
  • 如何捕获未处理的Promise错误方法
    小编给大家分享一下如何捕获未处理的Promise错误方法,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!为了保证可读性,本文采用意译而非直译,并且对源代码进行了大量修改。另外,本文版权归原作...
    99+
    2022-10-19
  • JavaScript中Promise的简单使用及其原理详解
    Promise是ES6最重要的特性之一,今天来系统且细致的研究一下Promise的用法以及原理。 按照我往常的理解,Promise是一个构造函数,有all、resolve、rejec...
    99+
    2023-03-23
    JavaScript Promise原理 JavaScript Promise使用 JavaScript Promise
  • Vue中如何优雅的捕获Promise异常详解
    目录常规的异常捕获方式好一些的方式:await-to-js更好的方式:全局捕获常规的异常捕获方式 在 Promise 提供了一个 .catch 方法用来捕获异常,假设有很多异步请求,...
    99+
    2022-11-13
    Vue Promise 异常捕获 Vue Promise
  • JS中promise特点与信任问题如何解决
    这篇文章主要介绍了JS中promise特点与信任问题如何解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JS中promise特点与信任问题如何解决文章都会有所收获,下面我们一起来看看吧。1.Promise的信...
    99+
    2023-07-02
  • JavaScript中Promise如何使用
    JavaScript中Promise如何使用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1.什么是 Promisepromise 是目前...
    99+
    2022-10-19
  • ES6的Promise如何使用
    本篇内容介绍了“ES6的Promise如何使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!所谓Promise,简单说就是一个容器,里面保存...
    99+
    2023-06-27
  • JavaScript中Promise的原理是什么及如何使用
    这篇文章主要介绍了JavaScript中Promise的原理是什么及如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaScript中Promise的原理是什么及如何使用文章都会有所收获,下面我们一起...
    99+
    2023-07-05
  • JavaScript中Promise如何处理异步的并行与串行
    这篇“JavaScript中Promise如何处理异步的并行与串行”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“JavaSc...
    99+
    2023-07-04
  • 如何上手Promise和aysnc/await
    如何上手Promise和aysnc/await,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。单线程,就是指一次只能完成一件任务,如果有多个任务...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作