• es6_Promise


    Promise

    Promise是一个创建promise对象的构造函数只能通过new关键字调用,不能直接调用,否则会报错,如下
    在这里插入图片描述

    三种状态

    promise存在三种状态:

    • pending(进行中)
    • fulfilled(已成功)
    • rejected(已失败)

    一旦状态改变,就不会再变!

    • 当promise处于pending状态时,状态可以改变为fulfilled或rejected。
    • 当pending->fulfilled或rejected状态就凝固了,不会再变了
      console.log(new Promise((resolve, reject)=>{
        resolve(111)
        reject(222)
      })) // Promise {< fulfilled >: 111}
      '
      运行
    语法
    new Promise(executor)
    

    executor:在构造函数中执行的 function。它接收两个函数作为参数:resolveFunc 和 rejectFunc。

    executor 中抛出的任何错误都会导致 Promise 被拒绝,并且返回值将被忽略

    执行过程
    • [1] 在通过new关键字实例化promise对象时,必须传入一个函数。

      函数在被执行时还会生成一对相应的 resolveFunc 和 rejectFunc 函数传入该函数中

       new Promise((resolve, reject)=>{})
      '
      运行

      Promise一旦被创建就会立即执行并且无法中途取消,此时promise的状态为pending

      executor 函数中的 return 语句仅影响控制流程,调整函数某个部分是否执行,但不会影响 Promise 的履行值

      const res = new Promise((resolve, reject)=>{
        return
        console.log(1111) // 不会执行
      })
      console.log(res) // Promise {}
      '
      运行

      如上示例中,return会终止executor函数的执行,但是不会改变Promise的状态。

      当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

    • [2] 状态改变

      Promise对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

      new Promise((resolve, reject)=>{
        // [1] 情况1 执行resolve函数时 Promise状态由pending转变为fulfilled
        // [2] 情况2 执行reject函数时,Promise状态由pending转变为rejected
        // [3] 状态3 抛出错误,Promise状态由pending转变为rejected(返回值被忽略)
      })
      '
      运行

      resolve、reject并不会终止 executor 函数后续执行

    js代码模拟
    class Promise{
      constructor(executor){
        this.value = undefined
        this.reason = undefined
        this.status = 'pending'
        const resoveFun = value => {
          if (this.status == 'pending') {
            this.value = value
            this.status = 'fulfilled'
          }
        }
        const rejectFun = reason =>{
          if (this.status == 'pending') {
            this.reason = reason
            this.status = 'rejected'
          }
        }
        try{
          executor(resoveFun, rejectFun)
        }catch(err){
          rejectFun(err)
        }
      }
    }
    '
    运行
    const res1 = new Promise((resove, reject)=>{})
    console.log(res1) // {status: pending}
    const res2 = new Promise((resove, reject)=>{ resove(111)})
    console.log(res2) // {status: 'fulfilled', value: 111}
    const res3 = new Promise((resove, reject)=>{ reject(222)})
    console.log(res3) // {status: 'rejected', reason: 222}
    const res4 = new Promise((resove, reject)=>{ resove(111) ; reject(222)})
    console.log(res4) // {status: 'fulfilled', value: 111}
    
    后续过程-链式调用
    then方法

    then方法是定义在原型对象Promise.prototype上的,作用是为 Promise 实例添加状态改变时的回调函数。

    语法
    Promise.then(onFulfilled, onRejected)
    

    then函数接收两个函数作为参数,分别是onFulfilled,onRejected

    • 当promise 状态为 fulfilled 时,调用 onFulfilled 函数
    • 当 promise 状态为 rejeted 时, 调用onRejected函数

    举例说明

    const p1 = new Promise((resolve, reject)=>{
    })
    p1.then(res=>{
      console.log(res)
    }, err=>{
      console.log(err)
    })
    '
    运行

    此时控制台没有任何值打印出来

    const p1 = new Promise((resolve, reject)=>{
      resolve(111)
    })
    p1.then(res=>{
      console.log(res)
    }, err=>{
      console.log(err)
    })
    '
    运行

    此时在控制台打印111

    const p1 = new Promise((resolve, reject)=>{
      reject(222)
    })
    p1.then(res=>{
      console.log(res)
    }, err=>{
      console.log(err)
    })
    '
    运行

    此时打印222

    注意点-then方法执行时机

    在执行过程中如果Promise状态变为resolved,则会在当前脚本所有同步任务执行完毕时会调用then方法指定的回调函数(并不是状态改变后立即调用)。

    new Promise(function (resolve, reject) {
      console.log(111)
      resolve(222)
      console.log(333)
    }).then(data => {
      console.log(data)
    })
    console.log(444)
    '
    运行

    执行结果为111、333、444、222

    执行111结束就resolve了,但是并没有马上执行then方法而是等待所有的同步函数执行结束才去执行then函数。

    注意点- then方法的执行顺序

    同一个 promise 可以注册多个 then 方法,当 promise 完成或者失败后,对应的 then 方法按照注册顺序依次执行

    const res = new Promise((resolve, reject)=>{
      resolve(111)
    })
    res.then(res=>{
      console.log('111', res)
    })
    res.then(res=>{
      console.log('222', res)
    })
    '
    运行

    执行结果在这里插入图片描述

    注意点-then方法的返回值

    then函数的返回值为一个新的Promise

      const p1 = new Promise(function (resolve, reject) {
          resolve(1)
        })
      console.log(p1.then(data => {})) // Promise ()
    '
    运行

    通过上述代码可以看到then函数的返回值为一个新的Promise,这样我们可以使用then方法进行链式调用

    const res = new Promise((resolve, reject)=>{
      resolve(111)
    })
    res.then(res=>{
      console.log('111', res) // ‘111’ 111
      return 222
    }).then(res=>{
      console.log('第二个res', res) // '第二个res' 222
    })
    '
    运行
    js代码模拟
    class Promise{
      constructor(executor){
        this.value = undefined
        this.reason = undefined
        this.status = 'pending'
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        const resoveFun = value => {
          if (this.status == 'pending') {
            this.value = value
            this.status = 'fulfilled'
            this.onFulfilledCallbacks.forEach((cb) => cb(this.value));
          }
        }
        const rejectFun = reason =>{
          if (this.status == 'pending') {
            this.reason = reason
            this.status = 'rejected'
            this.onRejectedCallbacks.forEach((cb) => cb(this.value));
          }
        }
        try{
          executor(resoveFun, rejectFun)
        }catch(err){
          rejectFun(err)
        }
      }
        
      then(onFulfilled, onRejected){
        if (this.status == 'fulfilled') {
          onFulfilled(this.value)
        }
        if (this.status == 'rejected') {
          onRejected(this.reason)
        }
        if (this.status == 'pending') {
          this.onFulfilledCallbacks.push(onFulfilled);
          this.onRejectedCallbacks.push(onRejected);
        }
      }
    }   
    '
    运行

    此处js代码模拟的Promise与Promise的区别是

    • [1] 真正的promise的then方法为微任务,此处无法模拟,因此在resolve/reject后会同步执行then方法而非异步
      const res = new Promise((resolve,reject)=>{
        console.log(111)
        resolve(222)
        console.log(333)
      })
      res.then(data=>{
        console.log('第一次:', data)
      })
      res.then(data=>{
        console.log('第二次', data)
      })
      console.log(444)
      '
      运行
      结果为1、3、2、2、4
      如果是真正的Promise实例化对象的话结果为1、3、4、2、2
    • [2] then的返回值不是Promise对象
      const res = new Promise((resolve,reject)=>{
        resolve(2)
      })
      console.log(res.then(data=>{})) // undefined
      '
      运行
    catch方法

    catch方法是定义在原型对象Promise.prototype上的,用于指定发生错误时的回调函数。

    1.在执行过程中如果Promise状态变为rejected,就会调用catch方法指定的回调函数.

    new Promise(function (resolve, reject) {
      throw new Error('test')
    })
    .then(data => {
      console.log('data', data)
    })
    .catch(err => {
      console.log('err', err) // err Error: test
    })
    '
    运行

    2.then方法里面如果出现异常也会走catch函数,类似与try/catch

    new Promise(function (resolve, reject) {
      resolve(111)
    })
    .then(data => {
      throw new Error('test')
    })
    .catch(err => {
      console.log('err', err) // err Error: test
    })
    '
    运行

    3.promise抛出的异常总会被后面的catch函数捕获,因此当使用链式调用时,最后跟随的catch方法可以捕捉前面任意then函数中的异常

    finally方法

    finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作,当不论是成功还是失败都会执行的代码就可以走这个函数。

    不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数

    finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果

    举例说明-链式调用

    需求:现在有两个请求,请求2需要请求1的请求结果作为参数,因此请求2需要等待请求1请求完毕之后再发送请求。

    • 因为Promise传入的函数是创建后立即执行,因此将不立即执行的封装在函数中,等待执行时机调用。
    • 当使用链式调用时,最后跟随的catch方法可以捕捉前面任意then函数中的异常
    const p1 = new Promise(function (resolve, reject) {
      // 模拟异步请求1-立即发送
      setTimeout(function () {
        resolve(222)
      }, 0)
    })
    
    function toP2 (value) {
      const p2 = new Promise(function (resolve, reject) {
        // 模拟异步请求2-封装在函数内部,等待请求1返回结果再发送
        setTimeout(function () {
          resolve(value)
        }, 0)
      })
      return p2
    }
    
    p1.then(data1 => {
      console.log('请求1获取的数据', data1) // 222
      return toP2(data1)
    }).then(data2 => {
      console.log('请求2获取的数据', data2) // 222
    }).catch(err=>{
      // 可以捕获前面所有then函数的异常
    })
    '
    运行
    后续过程-同时调用
    all方法

    有时我们代码中存在多个异步请求,希望等待多个异步请求的结果才能进行下一步操作,就可以使用all方法。

    语法

    Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.all([p1, p2, p3]);
    

    上面代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例

    结果

    p的状态由p1、p2、p3决定,分成两种情况。
    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    案例-成功
    function f1 () {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve('我是请求1的返回结果')
            }, 0)
          })
        }
        function f2 () {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve('我是请求2的返回结果')
            }, 500)
          })
        }
        Promise.all([f1(), f2()]).then(data => {
          console.log('data', data) // ["我是请求1的返回结果", "我是请求2的返回结果"]
        })
    '
    运行
    • 只有等待两个Promise都执行完毕才会走then方法,then回到函数里面的结果为1个数组,里面是按照顺序的返回结果。
    案例-失败
    function f1 () {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve('我是请求1的返回结果')
            }, 0)
          })
        }
        function f2 () {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              throw new Error('失败')
            }, 500)
          })
        }
        Promise.all([f1(), f2()])
          .then(data => {
            console.log('data', data)
          })
          .catch(err => {
            console.log('err', err) // err
          })
    
    • 只要>=1个失败就会走catch方法
    race方法

    有时我们代码中存在多个异步请求,希望等待多个异步请求中只要有1个请求的结果就能进行下一步操作,就可以使用race方法

    Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);
    

    上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
    Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。

    举例
    function f1 () {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve('我是请求1的返回结果')
            }, 0)
          })
        }
        function f2 () {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve('我是请求2的返回结果')
            }, 500)
          })
        }
        Promise.race([f1(), f2()])
          .then(data => {
            console.log('data', data) // data 我是请求1的返回结果
          })
          .catch(err => {
            console.log('err', err) 
          })
    '
    运行
    原理-源码解析
    思考题

    第一题:说出下列代码的输出结果

    • [1]

      console.log(new Promise())
      

      结果:error:Promise resolver undefined is not a function

      解析:Promise构造函数必须接受一个函数为参数,否则会报错

    • [2]

      console.log(new Promise(()=>{ return })) // 
      

      结果:Promise {< pending >}

      解析:Promise的函数会立即执行,此时promise的状态为pending,状态不受外界影响->只有异步操作的结果,可以决定当前是哪一种状态

    • [3]

      console.log(new Promise((resolve, reject)=>{
        resolve(111)
        reject(222)
      }))
      '
      运行

      结果: Promise {< fulfilled >: 111}

      解析: 当pending->fulfilled或rejected状态就凝固了,不会再变了

    第二题:说出数字的输出顺序

        console.log(1)  // 执行 打印1
        setTimeout(function () {
          console.log(2)
        }, 0) // 放入异步队列中
        new Promise(resolve => {
          // Promise在新建后就会立即执行(执行传入的函数)
          console.log(3) 
          resolve()
          console.log(4)
        }).then(() => {
          // 同步执行完毕后执行
          console.log(5)
        })
        // 同步代码-顺序执行
        console.log(6)
    '
    运行
    • Promise在新建后就会立即执行(执行传入的函数),then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行
    • 注意,调用resolve或reject并不会终结 Promise 的参数函数的执行
    • 定时器后面跟随的时间是等同步代码执行完毕后的等待时间。
    • 因此执行结果为1,3,4,6,5,2
  • 相关阅读:
    什么是用户体验测试? 为什么很重要?
    【java毕业设计】基于java+tomcat+jsp的威客网站设计与实现(毕业论文+程序源码)——威客网站
    8.11 Life Long Learning(终身学习)
    Proteus + Keil单片机仿真教程(六)多位LED数码管的动态显示
    java8之Consumer接口具有什么功能呢?
    upload-labs 16/17关
    Clickhouse 从S3/Hive导入数据
    论文解读(gCooL)《Graph Communal Contrastive Learning》
    使用 Next.js 和 React Router 构建单页应用程序(SPA)
    小米妙享无法正常启动,用非管理员权限启动
  • 原文地址:https://blog.csdn.net/qq_43260366/article/details/127095207