欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Promise的心得

程序员文章站 2022-06-23 09:30:15
这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar...

promise是什么?

从抽象的角度来说,它是一种新的解决方案。是解决JS中进行异步编程的解决方案。旧的解决方案就是:纯回调(但是promise里面也是有回调函数的,所以加了个),所以就有回调地狱产生。

从语法上来说: Promise 是一个构造函数。所以就可以创造出实例对象,然后指挥实例对象去做事情。反之,如果是一般的函数,则是函数本身去做事情。

从功能上来说: promise 对象用来封装一个异步操作并可以获取其结果

promise的状态改变:

  1. pending变为resolved
  2. pending变为rejected
  • 只有这 2 种状态
  • pending表示初始化状态
  • 且一个promise对象只能改变一次
  • 无论变为成功还是失败, 都会有一个结果数据
  • 成功的结果数据一般称为vlaue, 失败的结果数据一般称为reason

promise的基本流程:

Promise的心得
描述:

  1. 新建一个新的promise对象,而新建的promise对象的状态就是peding状态,即初始化状态
  2. promise构造函数传递一个参数,这是参数就是执行器函数,并在执行器函数中执行异步任务
  3. 执行异步任务,最终是有可能成功,也有可能失败。如果成功了,则去执行resolve()方法,然后promise对象的状态就变为了resolved状态;如果失败了,则去执行reject()方法,然后promise对象的状态就变为了rejected状态。
  4. 状态改变后,就去调用成功,或者失败的回调函数
  5. 成功或者失败的回调函数,就通过.then或者.catch指定。其中,.then既可以指定成功的回调,也可以指定失败的回调;但是.catch只能指定失败的回调
  6. 最终,.then又返回了一个新的promise对象

Promise的基本使用:

Promise的心得

为什么用Promise

纯回调:在你真正执行异步任务之前,你就得把回调函数事先指定好,也就是要先指定回调函数,然后再启动异步任务,而且必须是这样。

promise对象:可以在异步任务启动以后,再指定回调函数,但是是在异步任务成功,或者有结果之前再指定回调函数,当然,也可以在异步任务有了结果之后再指定也可以。这也是纯回调做不到的。也就是相对于纯回调函数,promise对象在指定回调函数的方式上更加的灵活。

总结:promise指定回调函数的方式更加灵活。一般是在异步任务执行完以后再指定回调函数。

纯回调:必须在启动异步任务前指定

Promise构造函数本身接收一个执行器函数excutor,而执行器函数excutor里面又接收两个函数,一个是resolve函数,一个是reject函数。且执行器函数excutor是同步回调函数,而真正的异步代码是写在执行器函数excutor里面的。

Promise的原型对象有一个then方法,指定两个参数onResolved, onRejected,且都是函数类型,更加准确一点是回调函数,一个对应成功的回调,一个对应失败的回调。成功的回调接收的是value,失败的回调接收的是reason,且返回的又是一个新的promise对象。这也是promise能够链式调用的前提。

promise支持链式调用,所以才可以解决回调地狱的问题。

什么是回调地狱?

答:回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件。回调地狱会涉及到多个异步操作,而且它是连续串联执行的 。什么是串联执行?假如有异步任务1,异步任务2,异步任务3…,其中第二个异步任务是以第一个异步任务的结果为条件,同样,第三个异步任务是以第二个异步任务为条件。此时就会产生回调的嵌套,代码组织形式也是逐渐向右,这种代码是难以阅读的。然后就是异常处理还要分别去处理,这样也会加大处理难度。这种后续处理麻烦,也不变阅读的情况就是回调地狱。

每得到一个promise对象,就说明启动了一个异步任务,或者说一个promise对应一个异步任务。然后通过.then编码方式就是从上往下执行,类似同步编码,就不是嵌套的形式,也就不会产生回调地狱。然后再处理异常的方式上,只需要在最后处理即可,也就是中间只需要些成功的回调即可。

//2.2. 使用promise的链式调用解决回调地狱
doSomething()
  .then(function(result) {
  return doSomethingElse(result)
})
  .then(function(newResult) {
  return doThirdThing(newResult)
})
  .then(function(finalResult) {
  console.log('Got the final result: ' + finalResult)
})
  .catch(failureCallback)

在最后处理异常的方式,也被称之为异常传透:在链式调用时,有多个promise的处理,而无论是任何一个出了问题,都会一层层传传到最下面错误回调的处理。

总结:promise处理,在编码上是从上往下的方式,阅读也就更加的方便,然后就是异常处理也会更加的方便。不过promise也不是解决回调地狱最优化的选择, 因为promise中还会有回调函数,虽然没有回调的嵌套了,但是还会有回调函数。然而用asyncawait就没有回调函数的产生了。

问:回调地狱的缺点?

答:不便于阅读 / 不便于异常处理

问:解决方案?

答:promise链式调用

问:终极解决方法?

答:async/await

  1. Promise构造函数: Promise (excutor) {}
    excutor执行器函数: 同步执行 (resolve, reject) => {}
    resolve函数: 内部定义成功时我们调用的函数 value => {}
    reject函数: 内部定义失败时我们调用的函数 reason => {}
    说明: excutor会在Promise内部执行同步回调,而异步操作是在执行器中执行的

  2. Promise.prototype.then方法: (onResolved, onRejected) => {}
    onResolved函数: 成功的回调函数 (value) => {}
    onRejected函数: 失败的回调函数 (reason) => {}
    说明: 指定用于得到成功value的成功回调,和用于得到失败reason的失败回调,并且返回一个新的promise对象,这也是pormise能够支持链式调用的前提。

  3. Promise.prototype.catch方法: (onRejected) => {}
    onRejected函数: 失败的回调函数 (reason) => {}
    说明: then()的语法糖, 相当于: then(undefined, onRejected)

  4. Promise.resolve方法: (value) => {}
    value: 成功的数据或promise对象
    说明: 返回一个成功/失败的promise对象,本质是语法糖。

//二者都返回一个promise
const p1 = new Promise((resolve, reject) => {
       setTimeout(() => {
        resolve(1)
       }, 100);
   })
const p2 = Promise.resolve(2)
  1. Promise.reject方法: (reason) => {}
    reason: 失败的原因
    说明: 返回一个失败的promise对象,本质是语法糖。
const p1 = new Promise((resolve, reject) => {
       setTimeout(() => {
        reject(1)
       }, 100);
   })
const p3 = Promise.reject(3)
  1. Promise.all方法: (promises) => {}
    promises: 包含npromise的数组

​ 说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败

const pAll = Promise.all([p1, p2])
pAll.then(
  //注意,此次的value是个复数,也就是说最后输出的 values 是个数组,起里面值的顺序和all里面promise数组一致,和执行完成的先后顺序没有关系,不过用Promise.race时,和执行完成的先后顺序有关系
  values => {
    console.log('all onResolved()', values)
  },
  reason => {
    console.log('all onRejected()', reason)
  }
)
  1. Promise.race方法: (promises) => {}
    promises: 包含npromise的数组
    说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态
//根据race这个单词的意思可知,这个方法更强调的是谁先完成,我就先返回它的值
const pRace = Promise.race([p1, p2, p3])
pRace.then(
  value => {
    console.log('race onResolved()', value)
  },
  reason => {
    console.log('race onRejected()', reason)
  }
)

回调函数:你定义的;你没有亲自调用;但是最终却执行了

promise几个关键问题

  1. 如何改变promise的状态?
  • resolve(value): 如果当前是pendding就会变为resolved
  • reject(reason): 如果当前是pendding就会变为rejected
  • 抛出异常: 如果当前是pendding就会变为rejected
const p = new Promise((resolve, reject) => {
  // resolve(1) // promise变为resolved成功状态
  // reject(2) // promise变为rejected失败状态
  // throw new Error('出错了') // 抛出异常, promse变为rejected失败状态, reason为 抛出的error
      throw 3 // 抛出异常, promse变为rejected失败状态, reason为 抛出的3
    })
    p.then(
      value => {},
      reason => {console.log('reason', reason)}
    )
    p.then(
      value => {},
      reason => {console.log('reason2', reason)}
    )
  1. 一个promise指定多个成功/失败回调函数, 都会调用吗?

答:是的。不过有条件,即当promise改变为对应状态时都会调用

  1. 改变promise状态和指定回调函数谁先谁后?

答:都有可能。正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调。

  • 如何先改状态,再指定回调?

    ①在执行器中直接调用resolve()/reject()

    ②延迟更长时间才调用then()

    new Promise((resolve, reject) => {
      	resolve(1) //先改变的状态(同时指定数据)
    }).then(// 后指定回调函数, 异步执行回调函数
      	value => {console.log('value2', value)},
      	reason => {console.log('reason2', reason)}
    )
    //验证 .then 里面的回调函数是同步执行,还是异步执行,结果:异步执行
    //即,执行器函数里面的 resolve(1)和 .then 都是同步执行,而 .then 里面的 value和 reason 回调函数都是异步执行
    console.log('-------')
    

    总结:promise的,无论是成功还是失败的回调函数,永远是异步执行的,即使条件已经满足了,即状态改变了,也不会马上执行。

  • 如何先指定回调函数, 后改变的状态?常规

     // 常规: 先指定回调函数, 后改变的状态
    new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
        }, 1000);
    }).then(// 先指定回调函数, 保存当前指定的回调函数
        value => {},
        reason => {console.log('reason', reason)}
    )
    
  • 什么时候才能得到数据?

    ①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据

    ②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据

  1. promise.then()返回的新promise结果状态由什么决定?(这个最重要)

(1)简单表达: 由then()指定的回调函数执行的结果决定
(2)详细表达:
​ ①如果抛出异常, 新promise变为rejected, reason为抛出的异常
​ ②如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
​ ③如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果

 new Promise((resolve, reject) => {
      // resolve(1)
      reject(1)
    }).then(
      value => {
        console.log('onResolved1()', value)
        // return 2
        // return Promise.resolve(3)
        // return Promise.reject(4)
        throw 5
      },
      reason => {
        console.log('onRejected1()', reason)
        // return 2
        // return Promise.resolve(3)
        // return Promise.reject(4)
        throw 5
      }
    ).then(
      value => {
        console.log('onResolved2()', value)
      },
      reason => {
        console.log('onRejected2()', reason)
      }
)
  1. promise如何串连多个操作任务?(有可能是同步任务,也有可能是异步任务)

答:(1)promisethen()返回一个新的promise, 可以看成then()的链式调用
(2)通过then的链式调用串连多个同步,或者异步任务。但是,如果是异步的,则一定要包在promise里面,因为promise就是用来封装异步操作的。(详见第二个.then内部的异步任务)

new Promise((resolve, reject) => {
      resolve(1)
    }).then(
      value => {
        console.log('onResolved1()', value)
        return 2 //同步操作
      },
    ).then(
      value => {
        console.log('onResolved12()', value)
        return new Promise((resolve,reject) =>{
          //注意:这是一个异步任务,所以必须用promise来封装
          setTimeout(() =>{
            resolve(3)
          },1000)
    	})
      },
    ).then(
      value => {
        console.log('onResolved3()', value)
      },
    )
})

注意:promise串联多个操作任务时,第一个promise.then()里面的回调函数类型,是不会影响第二个promise.then()里面的回调函数的,能影响第二个promise.then()里面的回调函数,只能是第一个promise.then()里面的回调函数的返回值

打个比方:

new Promise((resolve, reject) =>{
  resolve(1)
}).then(
  reason =>{
    return 2
  }
).then(
	value =>{
    console.log(value)
  },
  reason =>{
    console.log(reason)
  }
)

观察上面的代码:在第一个.then()里面,执行的是失败的回调函数onrejected,但是它的返回值却不是异常throw,或者 return Promise.resolve()),也就是虽然执行的是失败的回调,但是在下一个.then里面执行的却是成功的回调

  1. promise异常传/穿透?

(1)当使用promisethen链式调用时, 可以在最后指定失败的回调,
(2)前面任何操作出了异常, 都会传到最后失败的回调中处理

  1. 中断promise链?

(1)当使用promisethen链式调用时, 在中间中断, 不再调用后面的回调函数
(2)办法: 在回调函数中返回一个pendding状态的promise对象

return new Promise(() => {}) // 返回一个pending的promise  中断promise链

补充:箭头函数中的=>有个作用:return,前提是=>的右边没有{}

本文地址:https://blog.csdn.net/qq_41769706/article/details/107522852

相关标签: es6 javascript