Promise

57 阅读7分钟

1.promise是什么

(1)抽象表达: Promise是一门新的技术(es6规范);是JS种进行异步编程的新解决方案(旧方案是单纯使用回调函数) (2)具体表达:从语法上来说,Promise是一个构造函数;从功能上来说,Promise对象用来封装一个异步操作并可以获取其成功/失败的结果值

promise本身只是一个容器,真正异步的是它的两个回调resolve()和reject() **promise本质 不是控制 异步代码的执行顺序(无法控制) , 而是控制异步代码结果处理的顺序

异步操作包括:

  • 回调函数 把一个函数当作参数传递,传递的是函数的定义并不会立即执行,而是在将来特定的时机再去调用,这个函数就叫做回调函数。
  • 事件处理
  • Ajax请求
  • 定时器

2.为什么要用Promise

支持链式调用,可以解决回调地狱问题

什么是回到地狱?---回调函数的层层嵌套

1668750770810.png

回调地狱的缺点:

  • 不便于阅读
  • 不便于异常处理
  • 代码可复用性不强
  • 可维护性(迭代性差),扩展性差

3.Promise的初体验

如使用promise封装一个定时器

<body>
    <h1>promise</h1>
    <button id="btn">点我抽奖</button>
</body>
<script>
    // 定义一个随机数
    function rand (m,n){
        return Math.ceil(Math.random() * (n-m+1) + m-1)
    }
    let btn = document.querySelector("#btn")
    // 绑定点击事件
    btn.addEventListener('click',function(){
      // 使用promise封装
    const p = new Promise((resolve,reject)=>{
         // 定时器
         setTimeout(()=>{
            // 中奖概率1-100
            // 获取从1-100的一个随机数
            let n = rand(1,100)
            // // 判断
            if(n<=30) resolve(n)
           reject(n)

    })
 })
 //调用promise的then方法
 p.then((res)=>{
        alert('恭喜你中奖啦,奖品为10万元的购房优惠券,中奖号码为'+res)
    },(err)=>{
        alert('很遗憾,再试一次,本次抽奖号码为'+err)
     }) 
 })
</script>

4.Promise的状态

实例对象中的一个属性[PromiseState]

Promise的三种状态:

  • pending 未决定的/进行中
  • resolved/fullfilled 成功
  • rejected 失败

Promise状态的改变:

  • pending 变为 resolved
  • pending 变为 rejected

[说明:只有这两种,且一个Promisez对象只能改变一次,无论变为成功还是失败都会有一个结果数据]

5.Promise 对象的值

实例对象中的另一个属性[PromiseResult]

保存着异步任务[成功/失败]的结果

  • resolve
  • reject

6.Promise的基本流程

1668761206098.png

7.Promise的api

Promise.prototype.then(onResolved,onRejected)=>{}

注意:new Promise在实例化的过程中所执行的代码都是同步进行的,而then中注册的回调才是异步执行的。

then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。 then方法返回的是一个新的Promise实例,因此可以采用链式写法,即then方法后面再调用另一个then方法

Promise.prototype.catch(onRejected)=>{}

catch用于指定发生错误时的回调函数.,即如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。

1.  // 写法一
1.  const promise = new Promise(function(resolve, reject) {
1.  try {
1.  throw new Error('test');
1.  } catch(e) {
1.  reject(e);
1.  }
1.  });
1.  promise.catch(function(error) {
1.  console.log(error);
1.  });
1.  
1.  // 写法二
1.  const promise = new Promise(function(resolve, reject) {
1.  reject(new Error('test'));
1.  });
1.  promise.catch(function(error) {
1.  console.log(error);
1.  });

Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作(ES2018 引入)

Promise.resolve():返回一个成功的promise对象

如果传入的参数为非Promise对象,则返回的结果为成功promise对象;如果传入的参数为promise对象,则参数的结果决定了resolve的结果

Promise.reject():返回一个失败的promise对象

Promise.all(): 完成并发任务,参数为包含n个promise的数组

返回一个新的promise实例,只有所有的promise都成功才成功,只要有一个失败了就直接失败

Promise.race(): 参数为包含n个promise的数组

返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态

8.promise的几个关键问题

1.如何改变promise的状态

  • resolve:将promise状态由pending变为成功

  • reject:将promise状态由pending变为失败

  • 抛出异常:throw,如果当前是 pending 就会变为 rejected

2.一个promise指定多个成功或失败回调函数,都会调用吗

  • 会,当promise改变为对应状态时都会调用

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

  • 都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
  • 如何先改状态再指定回调?

(1)在执行器中做同步任务,直接调用reslove()/reject()

(2)延迟调用then()

  • 什么时候才能得到数据?

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

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

4.promise.then()返回的新promise的结果状态由什么决定

由then()指定的回调函数执行的结果状态决定

5.promise如何串连多个操作任务

(1)promise的then方法返回一个新的promise,可以写成then()的链式调用

(2)通过then的链式调用可以串联多个同步或异步任务

6.promise的异常穿透

(1)当使用promise的then链式调用时,可以在最后使用catch指定失败的回调

(2)前面任何操作出了异常都会传到最后失败的回调中处理

7.中断promise链 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数

方式:在回调函数中返回一个pending状态的promise对象

Promise.all、Promise.race、Promise.any的区别

all: 成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值

race: 哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

any: 返回最快的成功结果,如果全部失败就返回失败结果。

8.Promise的问题:

Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担,并且Promise传递中间值⾮常麻烦

Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的每⼀步。

所以ES2017推出了新的语法async/await 来更好的解决异步问题。

9.async和await

一句话概括:它就是 Generator 函数的语法糖,也就是处理异步操作的另一种高级写法

(1)async + 函数

async关键字: 修饰函数。 表示这个函数内部有异步操作。

  • 函数的返回值为promise对象
  • promise对象的结果由async函数执行的返回值决定
    • 如果返回值是一个非promise对象的数据,结果为成功的状态
    • 如果返回的是一个promise对象,由promise对象的返回结果决定
    • 抛出异常,throw,返回一个失败的结果和状态,通过try/catch处理失败的结果

(2)await + 表达式

await关键字: 等待异步执行完毕。

await只能用于被async修饰的函数中。 只有当await后面的异步操作执行完毕后,才会继续执行后面代码

1.await右侧的表达式一般为promise对象,但也可以是其他值

2.如果表达式是promise对象,await返回的是promise成功的值

3.如果表达式是其他的值,直接将此值作为await的返回值

async/await语法如下:

  1. 函数前面使用async修饰
  2. 函数内部,promise操作使用await修饰
  • await 后面是promise对象, 左侧的返回值就是这个promise对象的then方法中的结果
  • await必须要写在async修饰的函数中,不能单独使用,否则程序会报错
  • 如果await的promise失败了,就会抛出异常,需要通过.catch()或者try/catch捕获处理

async语法的异常捕获

async函数内部的异常可以通过 .catch()或者 try/catch来捕获,区别是

  • try/catch 能捕获所有异常,try语句抛出错误后会执行catch语句,try语句内后面的内容不会执行

  • catch()只能捕获异步方法中reject错误,并且catch语句之后的语句会继续执行