当前位置: 移动技术网 > IT编程>开发语言>其他编程 > Promise的心得

Promise的心得

2020年07月24日  | 移动技术网IT编程  | 我要评论

promise是什么?

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

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

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

promise的状态改变:

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

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构造函数本身接收一个执行器函数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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网