广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >怎么弄懂Promise原理
  • 915
分享到

怎么弄懂Promise原理

2024-04-02 19:04:59 915人浏览 薄情痞子
摘要

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

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

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。

基本过程:

  1.  初始化 Promise 状态(pending)

  2.  执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)

  3.  立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理

  4.  Promise中要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。

真正的链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise.

链式调用

先从 Promise 执行结果看一下,有如下一段代码:

new Promise((resolve, reject) => {        setTimeout(() => {            resolve({ test: 1 })            resolve({ test: 2 })            reject({ test: 2 })        }, 1000)    }).then((data) => {        console.log('result1', data)    },(data1)=>{        console.log('result2',data1)    }).then((data) => {        console.log('result3', data)    })    //result1 { test: 1 }    //result3 undefined

显然这里输出了不同的 data。由此可以看出几点:

  1.  可进行链式调用,且每次 then 返回了新的 Promise(2次打印结果不一致,如果是同一个实例,打印结果应该一致。

  2.  只输出第一次 resolve 的内容,reject 的内容没有输出,即 Promise 是有状态且状态只可以由pending -> fulfilled或 pending-> rejected,是不可逆的。

  3.  then 中返回了新的 Promise,但是then中注册的回调仍然是属于上一个 Promise 的。

基于以上几点,我们先写个基于 PromiseA+ 规范的只含 resolve 方法的 Promise 模型: 

function Promise(fn){          let state = 'pending';         let value = null;         const callbacks = [];         this.then = function (onFulfilled){             return new Promise((resolve, reject)=>{                 handle({ //桥梁,将新 Promise 的 resolve 方法,放到前一个 promise 的回调对象中                     onFulfilled,                      resolve                 })             })         }         function handle(callback){             if(state === 'pending'){                 callbacks.push(callback)                 return;             }             if(state === 'fulfilled'){                 if(!callback.onFulfilled){                     callback.resolve(value)                     return;                 }                 const ret = callback.onFulfilled(value) //处理回调                 callback.resolve(ret) //处理下一个 promise 的resolve             }         }         function resolve(newValue){             const fn = ()=>{                 if(state !== 'pending')return                 state = 'fulfilled';                 value = newValue                 handelCb()             }             setTimeout(fn,0) //基于 PromiseA+ 规范         }         function handelCb(){             while(callbacks.length) {                 const fulfiledFn = callbacks.shift();                 handle(fulfiledFn);             };         }         fn(resolve)     }

这个模型简单易懂,这里最关键的点就是在 then 中新创建的 Promise,它的状态变为 fulfilled 的节点是在上一个 Promise的回调执行完毕的时候。也就是说当一个 Promise 的状态被 fulfilled 之后,会执行其回调函数,而回调函数返回的结果会被当作 value,返回给下一个 Promise(也就是then 中产生的 Promise),同时下一个 Promise的状态也会被改变(执行 resolve 或 reject),然后再去执行其回调,以此类推下去…链式调用的效应就出来了。

但是如果仅仅是例子中的情况,我们可以这样写: 

new Promise((resolve, reject) => {          setTimeout(() => {              resolve({ test: 1 })          }, 1000)      }).then((data) => {          console.log('result1', data)         //dosomething          console.log('result3')      })      //result1 { test: 1 }      //result3

实际上,我们常用的链式调用,是用在异步回调中,以解决"回调地狱"的问题。如下例子:

new Promise((resolve, reject) => {    setTimeout(() => {      resolve({ test: 1 })    }, 1000)  }).then((data) => {    console.log('result1', data)    //dosomething    return test()  }).then((data) => {    console.log('result2', data)  })  function test(id) {    return new Promise(((resolve) => {      setTimeout(() => {        resolve({ test: 2 })      }, 5000)    }))  }  //基于第一个 Promise 模型,执行后的输出  //result1 { test: 1 }  //result2 Promise {then: ƒ}

用上面的 Promise 模型,得到的结果显然不是我们想要的。认真看上面的模型,执行 callback.resolve 时,传入的参数是 callback.onFulfilled 执行完成的返回,显然这个测试例子返回的就是一个 Promise,而我们的 Promise 模型中的 resolve 方法并没有特殊处理。那么我们将 resolve 改一下:

function Promise(fn){         ...        function resolve(newValue){            const fn = ()=>{                if(state !== 'pending')return                if(newValue && (typeof newValue === 'object' || typeof newValue === 'function')){                    const {then} = newValue                    if(typeof then === 'function'){                        // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve                        //相当于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调                        then.call(newValue,resolve)                        return                    }                }                state = 'fulfilled';                value = newValue                handelCb()            }            setTimeout(fn,0)        }        ...    }

用这个模型,再测试我们的例子,就得到了正确的结果: 

new Promise((resolve, reject) => {          setTimeout(() => {              resolve({ test: 1 })          }, 1000)      }).then((data) => {          console.log('result1', data)          //dosomething          return test()      }).then((data) => {          console.log('result2', data)      })      function test(id) {          return new Promise(((resolve, reject) => {              setTimeout(() => {              resolve({ test: 2 })              }, 5000)          }))      }      //result1 { test: 1 }      //result2 { test: 2 }

显然,新增的逻辑就是针对 resolve 入参为 Promise 的时候的处理。我们观察一下 test 里面创建的 Promise,它是没有调用 then方法的。从上面的分析我们已经知道 Promise 的回调函数就是通过调用其 then 方法注册的,因此 test 里面创建的 Promise 其回调函数为空。

显然如果没有回调函数,执行 resolve 的时候,是没办法链式下去的。因此,我们需要主动为其注入回调函数。

我们只要把第一个 then 中产生的 Promise 的 resolve 函数的执行,延迟到 test 里面的 Promise 的状态为 onFulfilled 的时候再执行,那么链式就可以继续了。所以,当 resolve 入参为 Promise 的时候,调用其 then 方法为其注入回调函数,而注入的是前一个 Promise 的 resolve 方法,所以要用 call 来绑定 this 的指向。

基于新的 Promise 模型,上面的执行过程产生的 Promise 实例及其回调函数,可以用看下表:

Promisecallback
P1[{onFulfilled:c1(第一个then中的fn),resolve:p2resolve}]
P2 (P1 调用 then 时产生)[{onFulfilled:c2(第二个then中的fn),resolve:p3resolve}]
P3 (P2 调用 then 时产生)[]
P4 (执行c1中产生[调用 test ])[{onFulfilled:p2resolve,resolve:p5resolve}]
P5 (调用p2resolve 时,进入 then.call 逻辑中产生)[]

有了这个表格,我们就可以清晰知道各个实例中 callback 执行的顺序是:

c1 -> p2resolve -> c2 -> p3resolve -> [] -> p5resolve -> []

以上就是链式调用的原理了。

reject

下面我们再来补全 reject 的逻辑。只需要在注册回调、状态改变时加上 reject 的逻辑即可。

完整代码如下: 

function Promise(fn){           let state = 'pending';          let value = null;          const callbacks = [];          this.then = function (onFulfilled,onRejected){              return new Promise((resolve, reject)=>{                  handle({                      onFulfilled,                       onRejected,                      resolve,                       reject                  })              })          }          function handle(callback){              if(state === 'pending'){                  callbacks.push(callback)                  return;              }              const cb = state === 'fulfilled' ? callback.onFulfilled:callback.onRejected;              const next = state === 'fulfilled'? callback.resolve:callback.reject;              if(!cb){                  next(value)                  return;              }              const ret = cb(value)              next(ret)          }          function resolve(newValue){              const fn = ()=>{                  if(state !== 'pending')return                  if(newValue && (typeof newValue === 'object' || typeof newValue === 'function')){                      const {then} = newValue                      if(typeof then === 'function'){                          // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve                          //相当于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调                          then.call(newValue,resolve, reject)                          return                      }                  }                  state = 'fulfilled';                  value = newValue                  handelCb()              }              setTimeout(fn,0)          }          function reject(error){              const fn = ()=>{                  if(state !== 'pending')return                  if(error && (typeof error === 'object' || typeof error === 'function')){                      const {then} = error                      if(typeof then === 'function'){                          then.call(error,resolve, reject)                          return                      }                  }                  state = 'rejected';                  value = error                  handelCb()              }              setTimeout(fn,0)          }          function handelCb(){              while(callbacks.length) {                  const fn = callbacks.shift();                  handle(fn);              };          }          fn(resolve, reject)      }

异常处理

异常通常是指在执行成功/失败回调时代码出错产生的错误,对于这类异常,我们使用 try-catch 来捕获错误,并将 Promise 设为 rejected 状态即可。

handle代码改造如下: 

function handle(callback){          if(state === 'pending'){              callbacks.push(callback)              return;          }          const cb = state === 'fulfilled' ? callback.onFulfilled:callback.onRejected;          const next = state === 'fulfilled'? callback.resolve:callback.reject;          if(!cb){              next(value)              return;          }          try {              const ret = cb(value)              next(ret)          } catch (e) {              callback.reject(e);          }        }

我们实际使用时,常习惯注册 catch 方法来处理错误,例:

new Promise((resolve, reject) => {       setTimeout(() => {           resolve({ test: 1 })       }, 1000)   }).then((data) => {       console.log('result1', data)       //dosomething       return test()   }).catch((ex) => {       console.log('error', ex)   })

实际上,错误也好,异常也罢,最终都是通过reject实现的。也就是说可以通过 then 中的错误回调来处理。所以我们可以增加这样的一个 catch 方法: 

function Promise(fn){          ...         this.then = function (onFulfilled,onRejected){             return new Promise((resolve, reject)=>{                 handle({                     onFulfilled,                      onRejected,                     resolve,                      reject                 })             })         }         this.catch = function (onError){             this.then(null,onError)         }         ...     }

Finally方法

在实际应用的时候,我们很容易会碰到这样的场景,不管Promise最后的状态如何,都要执行一些最后的操作。我们把这些操作放到 finally 中,也就是说 finally 注册的函数是与 Promise 的状态无关的,不依赖 Promise 的执行结果。所以我们可以这样写 finally 的逻辑: 

function Promise(fn){           ...          this.catch = function (onError){              this.then(null,onError)          }          this.finally = function (onDone){              this.then(onDone,onError)          }          ...     }

resolve 方法和 reject 方法

实际应用中,我们可以使用 Promise.resolve 和 Promise.reject 方法,用于将于将非 Promise 实例包装为 Promise 实例。如下例子:

Promise.resolve({name:'winty'})  Promise.reject({name:'winty'})  // 等价于  new Promise(resolve => resolve({name:'winty'}))  new Promise((resolve,reject) => reject({name:'winty'}))

这些情况下,Promise.resolve 的入参可能有以下几种情况:

  •  无参数 [直接返回一个resolved状态的 Promise 对象]

  •  普通数据对象 [直接返回一个resolved状态的 Promise 对象]

  •  一个Promise实例 [直接返回当前实例]

  •  一个thenable对象(thenable对象指的是具有then方法的对象) [转为 Promise 对象,并立即执行thenable对象的then方法。]

基于以上几点,我们可以实现一个 Promise.resolve 方法如下: 

function Promise(fn){           ...          this.resolve = function (value){              if (value && value instanceof Promise) {                  return value;              } else if (value && typeof value === 'object' && typeof value.then === 'function'){                  let then = value.then;                  return new Promise(resolve => {                      then(resolve);                  });              } else if (value) {                  return new Promise(resolve => resolve(value));              } else {                  return new Promise(resolve => resolve());              }          }          ...      }

Promise.reject与Promise.resolve类似,区别在于Promise.reject始终返回一个状态的rejected的Promise实例,而Promise.resolve的参数如果是一个Promise实例的话,返回的是参数对应的Promise实例,所以状态不一 定。

因此,reject 的实现就简单多了,如下: 

function Promise(fn){           ...          this.reject = function (value){              return new Promise(function(resolve, reject) {                  reject(value);              });          }          ...      }

Promise.all

入参是一个 Promise 的实例数组,然后注册一个 then 方法,然后是数组中的 Promise 实例的状态都转为 fulfilled 之后则执行 then 方法。这里主要就是一个计数逻辑,每当一个 Promise 的状态变为 fulfilled 之后就保存该实例返回的数据,然后将计数减一,当计数器变为 0 时,代表数组中所有 Promise 实例都执行完毕。

function Promise(fn){         ...        this.all = function (arr){            var args = Array.prototype.slice.call(arr);            return new Promise(function(resolve, reject) {                if(args.length === 0) return resolve([]);                var remaining = args.length;                function res(i, val) {                    try {                        if(val && (typeof val === 'object' || typeof val === 'function')) {                            var then = val.then;                            if(typeof then === 'function') {                                then.call(val, function(val) {                                    res(i, val);                                }, reject);                                return;                            }                        }                        args[i] = val;                        if(--remaining === 0) {                            resolve(args);                        }                    } catch(ex) {                        reject(ex);                    }                }                for(var i = 0; i < args.length; i++) {                    res(i, args[i]);                }            });        }        ...    }

Promise.race

有了 Promise.all 的理解,Promise.race 理解起来就更容易了。它的入参也是一个 Promise 实例数组,然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行。因为 Promise 的状态只能改变一次,那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法,注入到数组中的每一个 Promise 实例中的回调函数中即可。

function Promise(fn){       ...      this.race = function(values) {          return new Promise(function(resolve, reject) {              for(var i = 0, len = values.length; i < len; i++) {                  values[i].then(resolve, reject);              }          });      }      ...      }

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

--结束END--

本文标题: 怎么弄懂Promise原理

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

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

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

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

下载Word文档
猜你喜欢
  • 怎么弄懂Promise原理
    这篇文章主要介绍“怎么弄懂Promise原理”,在日常操作中,相信很多人在怎么弄懂Promise原理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么弄懂Promise原理”...
    99+
    2022-10-19
  • 一文快速弄懂webpack动态import原理
    目录前言例子1. 模块加载2. jsonp动态加载script3. 执行异步脚本4. webpackJsonpCallback5. 执行异步模块代码流程图总结前言 在vue中我们经常...
    99+
    2022-11-13
  • 彻底弄懂Base64的编码与解码原理
    Base64是一种用于将二进制数据转换为可打印字符的编码方式。它使用64个字符来表示任意二进制数据,包括字母(大写和小写)、数字以及...
    99+
    2023-09-23
    Base64
  • 一文带你弄懂Java中线程池的原理
    目录为什么要用线程池线程池的原理ThreadPoolExecutor提供的构造方法ThreadPoolExecutor的策略线程池主要的任务处理流程ThreadPoolExecuto...
    99+
    2022-12-08
    Java线程池原理 Java线程池
  • 一文彻底弄懂零拷贝原理以及java实现
    目录零拷贝 传统I/O操作存在的性能问题 零拷贝技术原理 虚拟内存 mmap/write 方式 sendfile 方式 带有 scatter/gather 的 sendfile方式 ...
    99+
    2022-11-12
  • 一篇文章弄懂JVM类加载机制过程以及原理
    目录一、做一个小测试二、类的初始化步骤:三、看看你写对了没?四、类的加载过程五、类加载器的分类1、启动类加载器(引导类加载器)2、扩展类加载器3、应用程序类加载器(系统类加载器)六、...
    99+
    2023-02-07
    jvm加载类的过程和机制 jvm加载类原理机制 java类加载原理
  • javascript中Promise原理是什么
    本篇内容主要讲解“javascript中Promise原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“javascript中Promise原理是什么”吧...
    99+
    2022-10-19
  • 【锟斤拷�⊠是怎样炼成的】——两分钟帮你彻底弄懂计算机的编码原理
    📢📢📢📣📣📣 🌻🌻🌻Hello,大家好,我是天寒雨落,一名有趣的博主,小白一枚,多多关照&...
    99+
    2023-09-26
    c语言 python java 开发语言 windows
  • JavaScript promise的使用方法和原理是什么
    这篇“JavaScript promise的使用方法和原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Ja...
    99+
    2023-07-06
  • 怎么理解ES6 Promise对象
    这篇文章主要讲解了“怎么理解ES6 Promise对象”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解ES6 Promise对象”吧!概述是异步编程的...
    99+
    2022-10-19
  • 怎么理解Promise中的core.js
    这篇文章主要讲解了“怎么理解Promise中的core.js”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解Promise中的core.js”吧!源码...
    99+
    2022-10-19
  • 怎么搞懂Linux内存管理
    今天就跟大家聊聊有关怎么搞懂Linux内存管理,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。内存管理应该是Linux内核中非常重要的子系统,之前一直在构思怎么去写一篇Linux内存管...
    99+
    2023-06-16
  • JavaScript中Promise的原理是什么及如何使用
    这篇文章主要介绍了JavaScript中Promise的原理是什么及如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaScript中Promise的原理是什么及如何使用文章都会有所收获,下面我们一起...
    99+
    2023-07-05
  • 阿里云服务器还原系统怎么弄
    以下是使用阿里云服务器停止实例、停止云盘的步骤: 选择阿里云服务器并登录。 找到“更换 系统 盘”,并单击它。 在弹出的窗口中,选择“系统”盘并点击“停止”按钮。 等待系统停止并重新启动实例。 重新创建一个新的云盘,将其命名为“新系统”...
    99+
    2023-10-27
    阿里 怎么弄 服务器
  • 域名空间代理怎么弄
    域名空间代理是指将一个域名映射到另一个域名的服务,通常用于将一个已有的域名指向一个新的网站或服务器。具体实现方法如下:1. 在域名注...
    99+
    2023-06-07
    域名空间代理 域名 空间
  • 阿里云 怎么弄 代理ip
    一、阿里云代理IP的种类 VPN代理IP VPN代理IP是一种加密的代理IP,可以帮助用户在网络上隐藏自己的IP地址,从而更好地保护用户的隐私和安全。目前,阿里云提供了多种VPN代理IP,例如TENX、NETGEAR、TORCEL等。...
    99+
    2023-10-27
    阿里 怎么弄 ip
  • 阿里云服务器还原系统怎么弄的
    一、阿里云服务器还原系统的工作原理 阿里云服务器还原系统的工作原理主要包括以下几个方面: 硬件设备维护:阿里云服务器还原系统采用了硬件设备来保障还原效果。在还原过程中,需要使用硬件设备来检测系统状态和配置信息,并通过操作系统、应用程序等...
    99+
    2023-10-27
    阿里 怎么弄 服务器
  • 阿里云代理出价怎么弄
    一、阿里云代理出价的概念 阿里云代理出价是指阿里云公司为客户提供的一种购买云计算资源的方式。在购买云计算资源时,客户需要向阿里云公司支付一定的费用,以获得相应的代理权。这种购买方式通常包括购买和管理多个云计算服务,以及与阿里云公司进行结算...
    99+
    2023-10-27
    阿里 怎么弄
  • 阿里云代理授权怎么弄
    阿里云代理授权的好处是显而易见的。首先,阿里云代理可以提供稳定可靠的云计算服务,为企业提供可靠的技术支持和保障。其次,阿里云代理可以帮助企业节省成本,降低运营和维护成本,提高企业的运营效率和经济效益。此外,阿里云代理还可以提供定制化的服务,...
    99+
    2023-10-27
    阿里 怎么弄
  • 阿里云网盘代理怎么弄
    首先,成为阿里云网盘代理商需要具备一定的资质和信誉。代理商需要注册一个合法的公司,具有相关的资质和证书,如ISO质量管理体系认证、ISO信息安全管理体系认证等。同时,代理商需要了解和遵守阿里云公司的相关规定和要求,如服务费用、数据备份和恢复...
    99+
    2023-10-28
    阿里 怎么弄 云网
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作