Promise规范与原理解析

这篇具有很好参考价值的文章主要介绍了Promise规范与原理解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

Promise对象用于清晰的处理异步任务的完成,返回最终的结果值,本次分享主要介绍Promise的基本属性以及Promise内部的基础实现,能够帮我们更明确使用场景、更快速定位问题。

Promise出现的原因

首先我们先来看一段代码:异步请求的层层嵌套

function fn1(params) {
  const xmlHttp = new XMLHttpRequest();
  xmlHttp.onreadystatechange = function(){
    if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
      const fn1Data = {name: 'fn1'}
      console.log(fn1Data, 'fn1Data');
      // 请求2
      (function fn2() {
        xmlHttp.onreadystatechange = function(){
        if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
          const fn2Data = {name: `${fn1Data.name}-fn2`}
          console.log(fn2Data, 'fn2Data');
          // 请求3
          (function fn2() {
            xmlHttp.onreadystatechange = function(){
            if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
              const fn3Data = {name: `${fn2Data.name}-fn3`}
              console.log(fn3Data, 'fn3Data');
            }
          }
          xmlHttp.open("GET","https://v0.yiketianqi.com/api?unescape=1&version=v61", true);
          xmlHttp.send();
          })()
        }
      }
      xmlHttp.open("GET","https://v0.yiketianqi.com/api?unescape=1&version=v61", true);
      xmlHttp.send();
      })()
    }
  }
  xmlHttp.open("GET","https://v0.yiketianqi.com/api?unescape=1&version=v61", true);
  xmlHttp.send();
}

fn1()



或者我们可以将上面的代码优化为下面这样

function fn1(params) {
  console.log(`我是fn1,我在函数${params}中执行!!!`);
}
  
function fn2(params) {
  try {
    const xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function(){
      if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
        console.log(`我是fn2,我在函数${params}中执行!!!结果是:`,params.data);
        fn1('fn2')
      }
    }
    xmlHttp.open("GET","https://v0.yiketianqi.com/api?unescape=1&version=v61", true);
    xmlHttp.send();
  } catch (error) {
    console.error(error);
  }
}
  
function fn3() {
  try {
    const xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function(){
      if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
          console.log('fn3请求已完成');
          fn2('fn3')
      }
    }
    xmlHttp.open("GET","https://v0.yiketianqi.com/api?unescape=1&version=v61", true);
    xmlHttp.send();
    console.log('我是f3函数呀');
  } catch (error) {
    console.error(error);
  }
}
  
fn3()



由上面的两种写法的请求可见,在promise之前,为了进行多个异步请求并且依赖上一个异步请求的结果时,我们必须进行层层嵌套,大多数情况下,我们又对异步结果进行数据处理,这样使得我们的代码非常难看,并且难以维护,这就形成了回调地狱,由此Promise开始出现了。

回调地狱缺点

  • 代码臃肿
  • 可读性差
  • 耦合性高
  • 不好进行异常处理

Promise的基本概念

含义

  1. ES6将其写进了语言标准里统一了用法,是一个构造函数,用来生成Promise实例
  2. 参数为一个执行器函数(执行器函数是立即执行的),该函数有两个函数作为参数,第一个参数是成功时的回调,第二个参数是失败时的回调
  3. 函数的方法有resolve(可以处理成功和失败)、reject(只处理失败)、all等方法
  4. then、catch、finally方法为Promise实例上的方法

状态

  1. pending --- 等待状态
  2. Fulfilled --- 执行状态 (resolve回调函数,then)
  3. Rejected --- 拒绝状态 (reject回调函数,catch)
  4. 状态一旦改变就不会再变,状态只可能是两种改变,从pending->Fulfilled,pending->Rejected
  5. 有两个关键的属性:PromiseState --- 状态改变,PromiseResult --- 结果数据改变
const p1 = Promise.resolve(64)
const p2 = Promise.reject('我错了')
const p3 = Promise.then()
const p4 = Promise.catch()

// 状态改变PromiseState 结果改变PromiseResult
console.log(new Promise(()=>{}), 'Promise');  // PromiseState='pending' PromiseResult=undefined
console.log(p1,'p1');  // PromiseState='Fulfilled' PromiseResult=64
console.log(p2,'p2');  // PromiseState="Rejected" PromiseResult='我错了'
console.log(p3, 'p3'); // then为实例上的方法,报错
console.log(p4, 'p4');  // catch为实例上的方法,报错


特点

  1. 错误信息清晰定位:可以在外层捕获异常信息(网络错误、语法错误都可以捕获),有“冒泡”性质,会一直向后传递,直到被捕获,所以在最后写一个catch就可以了

  2. 链式调用:每一个then和catch都会返回一个新的Promise,把结果传递到下一个then/catch中,因此可以进行链式调用 --- 代码简洁清晰

结果由什么决定

resolve

  1. 如果传递的参数是非Promise类型的对象,则返回的结果是成功状态的Promise对象,进入下一个then里面
  2. 如果传递的参数是Promise类型的对象,则返回的结果由返回的Promise决定,如果返回的是resolve则是成功的状态,进入下一个then里,如果返回的是reject则是失败的状态,进入下一个catch里

reject

  1. 如果传递的参数是非Promise类型的对象,则返回的结果是拒绝状态的Promise对象,进入下一个catch里面或者是下一个then的第二个参数reject回调里面
  2. 如果传递的参数是Promise类型的对象,则返回的结果由返回的Promise决定,如果返回的是resolve则是成功的状态,进入下一个then里,如果返回的是reject则是拒绝的状态,进入下一个catch里面或者是下一个then的第二个参数reject回调里面

这在我们自己封装的API里面也有体现:为什么code为1时都是then接收,其他都是catch接收,就是因为在then里面也就是resolve函数中对code码进行了判断,如果是1则返回Promise.resolve(),进入then里处理,如果是非1则返回Promise.reject(),进入catch里处理。

流程图

简单使用

// 模拟一个promise的get请求
let count = 0
function customGet(url){
    count += 1
    return new Promise((resolve, reject)=>{
        const xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET",url, true);
        xmlHttp.onload = ()=>{
          console.log(xmlHttp, 'xmlHttp---onload');
          if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
            console.log('customGet请求成功了');
            // 返回非Promise,结果为成功状态
            resolve({data:`第${count}次请求获取数据成功`})

            // 返回Promise,结果由Promise决定
            // resolve(Promise.reject('resolve中返回reject'))
          } else {
            reject('customGet请求错误了')
          }
        }

        // Promise状态改变就不会再变
        // onreadystatechange方法会被执行四次
        // 当地次进来的时候,readyState不等于4,执行else逻辑,执行reject,状态变为Rejected,所以即使再执行if,状态之后不会再改变
        // xmlHttp.onreadystatechange = function(){
        //   console.log(xmlHttp,'xmlHttp---onreadystatechange')
        //   if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
        //     console.log('customGet请求成功了');
        //     resolve({data:`第${count}次请求获取数据成功`})
        //   } else {
        //     reject('customGet请求错误了')
        //   }
        // }
        xmlHttp.send();
      })
 }

// 使用Promise,并且进行链式调用
customGet('https://v0.yiketianqi.com/api/cityall?appid=&appsecret=').then((res)=>{
   console.log(res.data);
   return '第一次请求处理后的数据'
}).then((data)=>{
   console.log(data)
   // console.log(data.toFixed());
   return customGet('https://v0.yiketianqi.com/api/cityall?appid=&appsecret=')
}).then((res)=>{
   console.log(res.data);
}).catch((err)=>{
    // 以类似'冒泡'的性质再外层捕获所有的错误
   console.error(err, '这是catch里的错误信息');
})



手写实现简单的Promise

通过上面的回顾,我们已经了解了Promise的关键属性和特点,下面我们一起来实现一个简单的Promise吧

  // 1、封装一个Promise构造函数,有一个函数参数
  function Promise(executor){
    // 7、添加对象属性PromiseState PromiseResult
    this.PromiseState = 'pending'
    this.PromiseResult = null

    // 14、创建一个保存成功失败回调函数的属性
    this.callback = null

    // 8、this指向问题
    const that = this

    // 4、executor有两个函数参数(resolve,reject)
    function resolve(data){
      // 10、Promise状态只能修改一次(同时记得处理reject中的状态)
      if(that.PromiseState !== 'pending') return

      // console.log(this, 'this');
      // 5、修改对象的状态PromiseState
      that.PromiseState = 'Fulfilled'

      // 6、修改对象的结果PromiseResult
      that.PromiseResult = data

      // 15、异步执行then里的回调函数
      if(that.callback?.onResolve){
        that.callback.onResolve(that.PromiseResult)
      }
    }
    function reject(data){
      console.log(that.PromiseState, 'that.PromiseState');
      if(that.PromiseState !== 'pending') return

      // 9、处理失败函数状态
      that.PromiseState = 'Rejected'
      that.PromiseResult = data
      console.log(that.PromiseResult, 'that.PromiseResult');
      console.log(that.PromiseState, 'that.PromiseState');

      // 16、异步执行then里的回调函数
      if(that.callback?.onReject){
        that.callback.onReject(that.PromiseResult)
      }
    }
    // 3、执行器函数是同步调用的,并且有两个函数参数
    executor(resolve,reject)
  }
  // 2、函数的实例上有方法then
  Promise.prototype.then = function(onResolve,onReject){
    // 20、处理onReject没有的情况
    if(typeof onReject !== 'function'){
      onReject = reason => {
        throw reason
      }
    }
    // 21、处理onResolve没有的情况
    if(typeof onResolve !== 'function'){
      onResolve = value => value
    }
    // 17、每一个then方法都返回一个新的Promise,并且把上一个then返回的结果传递出去
    return new Promise((nextResolve,nextReject)=>{
      // 11、处理成功或失败
      if(this.PromiseState === 'Fulfilled'){
        // 12、将结果传递给函数
        // onResolve(this.PromiseResult)

        // 18、拿到上一次执行完后返回的结果,判断是不是Promise
        const result = onResolve(this.PromiseResult)
        if(result instanceof Promise){
          result.then((v)=>{
            nextResolve(v)
          },(r)=>{
            nextReject(r)
          })
        } else {
          nextResolve(result)
        }
      }
      // 当你一步步写下来的时候有没有怀疑过为什么不用else
       if(this.PromiseState === 'Rejected'){
            // 第12步同时处理此逻辑
            // onReject(this.PromiseResult)

            // 22、处理catch异常穿透捕获错误
            try {
              const result = onReject(this.PromiseResult)
              if(result instanceof Promise){
                result.then((v)=>{
                  nextResolve(v)
                }).catch((r)=>{
                  nextReject(r)
                })
              } else {
                nextReject(result)
              }
            } catch (error) {
              nextReject(this.PromiseResult)
            }
         }
  
      // 13、异步任务时处理成功或失败,想办法等异步任务执行完成后才去执行这两个函数
      if(this.PromiseState === 'pending'){
        this.callback = {
          onResolve,
          onReject
        }
        console.log(this.callback, 'this.callback');
      }
    })
  }
  // 19、函数实例上有方法catch
  Promise.prototype.catch = function(onReject) {
    return this.then(null,onReject)
  }

  // 使用自定义封装的Promise
  const customP = new Promise((resolve,reject)=>{
    // 模拟异步执行请求
    // const xmlHttp = new XMLHttpRequest();
    // xmlHttp.open("GET",'https://v0.yiketianqi.com/api/cityall?appid=&appsecret=', true);
    // xmlHttp.onload = ()=>{
    //   if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
    //     resolve('success')
    //   } else {
    //     reject('error')
    //   }
    // }
    // xmlHttp.send();

    // 同步执行
    resolve('success')
    // reject('error')
  })

  console.log(customP, 'customP');
  customP.then((res)=>{
    console.log(res, 'resolve回调');
    return '第一次回调'
    // return new Promise((resolve,reject)=>{
    //   reject('错错错')
    // })
  },(err)=>{
    console.error(err, 'reject回调');
    return '2121'
  }).then(()=>{
    console.log('then里面输出');
  }).then().catch((err)=>{
    console.error(err, 'catch里的错误');
  })



针对resolve中返回Promise对象时的内部执行顺序

总结

以上就是我们常用的Promise基础实现,在实现过程中对比了Promise和函数嵌套处理异步请求的优缺点,Promise仍存在缺点,但是的确方便很多,同时更清晰的理解到错误处理如何进行异常穿透的,也能帮助我们更规范的使用Promise以及快速定位问题所在。

作者:京东物流 孙琦

来源:京东云开发者社区 自猿其说Tech 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-746969.html

到了这里,关于Promise规范与原理解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

推荐阅读

  • 【论文阅读】DEPCOMM:用于攻击调查的系统审核日志的图摘要(S&P-2022)
  • Promise对象的使用
  • 前端异步编程 —— Promise对象
  • ES6---Promise对象
  • 前端多线程处理 —— Promise对象

热门推荐

  • 使用Java将int数组转换为IntStream详细解说

    使用Java将int数组转换为IntStream详细解说

  • localhost与127.0.0.1的区别及IPv6中的对应情况

    localhost与127.0.0.1的区别及IPv6中的对应情况

  • 零基础学习谷歌SEO的全面指南:提升网站排名和流量

    零基础学习谷歌SEO的全面指南:提升网站排名和流量

  • 个人电脑与服务器:CPU的差异深度解析

    个人电脑与服务器:CPU的差异深度解析

  • LangChain输出解析器与链的深度解析

    LangChain输出解析器与链的深度解析

  • 用户需求与搜索排名:百度技术判定的深度解析

    用户需求与搜索排名:百度技术判定的深度解析

  • 用户需求与SEO - 如何通过满足用户需求提升网站排名

    用户需求与SEO - 如何通过满足用户需求提升网站排名

  • Unity中实现滑块跟随物体的算法解析

    Unity中实现滑块跟随物体的算法解析

  • OpenAI 发布 GPT-4o:多模态AI模型,实现自然人机交互

    OpenAI 发布 GPT-4o:多模态AI模型,实现自然人机交互

  • 免费、开源、好用的SQL客户端合集:精选工具推荐

    免费、开源、好用的SQL客户端合集:精选工具推荐

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包

玻璃钢生产厂家汕头步行街玻璃钢雕塑湖北镜面不锈钢玻璃钢仿铜雕塑南通设计玻璃钢雕塑费用绵阳玻璃钢卡通雕塑厂家商场美陈发光球常州玻璃钢雕塑批量定做玻璃钢卡通雕塑材质高质量的玻璃钢雕塑加工厂德州玻璃钢景观雕塑定做价格安徽欧式玻璃钢雕塑哪家便宜湖南玻璃钢牛动物雕塑小猪摆件长治玻璃钢仿铜雕塑价格山东玻璃钢海豚雕塑定制内江成都商场美陈流程合肥商场美陈费用郑州景观玻璃钢雕塑定制宁德玻璃钢卡通雕塑昆明市玻璃钢雕塑怎么样商场美陈和平面设计哪个好襄阳商场美陈品质玻璃钢雕塑销售厂家邯郸玻璃钢仿铜雕塑价格杭州商场美陈济南仿铜玻璃钢雕塑安装福建卡通玻璃钢雕塑定制四川玻璃钢雕塑商家玻璃钢雕塑定做厂家电话西宁景区玻璃钢雕塑价格玻璃钢传统人物雕塑热线电话荷花鱼玻璃钢雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化