• Promise及相关知识细学


    学习关键语句:
    Promise
    promise学习
    promise.all
    promise.race
    promise.resolve

    1. 写在前面

    promise 是前端绕不开的东西 , 所以我们一定要好好学习 , 写这篇文章的目的是加深对 promise 的学习和使用程度

    2. 开始

    2.1 准备

    首先创建一个文件夹 , 里面新建一个 index.html 和 index.js
    我们在浏览器环境观看 promise 的使用效果
    在这里插入图片描述

    2.2 看

    我们要先看一看这个 promise 到底是啥

    let o1 = new Object()
    console.log(o1)
    
    let p1 = new Promise((resolve, reject) => { })
    console.log(p1)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们可以看到 promise 比正常的对象要多了两个东西 , 一个是 PromiseState , 值为 pending ; 一个是 PromiseResult , 值为 undefined

    在这里插入图片描述

    我们从实例化方式和打印对象中可以看出 , promise 是一个构造函数或者说类 , 看上去只不过是在实例化时传入一个函数而已 , 那么我们继续看一看一个简单的 promsie 例子后 , 我们再看这个 promise 变成什么了

    let x = 1
    let p1 = new Promise((resolve, reject) => {
      if (x < 2) {
        resolve(x)
      } else {
        reject(x)
      }
    })
    p1.then(value => {
      console.log('value:' + value) // value:1
    }, reason => {
      console.log('reason:' + reason)
    })
    console.log(p1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们看到 PromiseState 变成了 fulfilled 而 PromiseResult 变成了 1

    在这里插入图片描述

    这好像没什么大不了的嘛 , 乍一看就跟乍一看一样 , 好像理所当然应该是这样的

    接下来我们将 prototype 点开 ,看一看原型上有哪些方法

    在这里插入图片描述

    原型上的方法不多嗷 , 我们也只需要关注 catch finally 和 then 方法就够了
    ok 那么我们已经完全看到了 promise 实例的相关内容 , 接下来我们来看一看 promise 构造器上有什么内容
    点开 constructor

    在这里插入图片描述

    构造器上的方法我们需要关注的就是 all allSettled any race reject 和 resolve 方法

    好啦 , 这下 promise 已经被你完全看完了 , 到这里大致混个眼熟就 ok , 接下来才开始学习

    2.3 基础知识

    可能你会有所疑惑 , 所以接下来介绍一下基础知识

    一、PromiseState

    一个 promise 有三个状态 , 分别是 pending , fulfilled 和 rejected

    1. pending 是一个 promise 的基础状态 , 所有 promise 一开始都是 pending 状态
      理解为 一个承诺即将发生
    2. fulfilled 表示这个 promise 已完成并且执行成功的回调函数
      理解为 一个承诺守约履行了
    3. rejected 表示这个 promise 失败了并且执行失败的回调函数
      理解为 一个承诺毁约拒绝了

    特别注意:

    • 一个 promise 只能有一个状态 , 即一个承诺不会 即将发生 \ 守约 \ 毁约 同时出现
    • 状态 能从 pending 向 fulfilled rejected 改变
    • 状态一旦发生改变 , 将不会发生第二次改变 , 即状态固化了

    二、PromiseResult

    顾名思义 , 其实指的就是一个 promise 的结果值

    • 承诺守约履行了值就为 成功回调传入的参数
    • 承诺毁约拒绝了值就为 失败回调传入的参数
    • 由于状态不会发生二次改变 , 所以结果值是不会变的 , 无论多少次调用都会是同一结果

    三、promise中的两个参数函数

    我们看以下代码

    let x = 1;
    let p1 = new Promise((resolve, reject) => {
      if (x < 2) {
        resolve(x);
      } else {
        reject('出错了');
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    p1 是我们新实例化的一个 promise 对象 , 和正常实例化一样的是 , 我们在初始化时传递了一个参数进去 , 只不过这个参数是一个函数 , 喏就是下面这个函数 , 是不是这样看起来就好很多

    (resolve, reject) => {
      if (x < 2) {
        resolve(x);
      } else {
        reject('出错了');
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个函数一共有两个参数 , resolve 和 reject , 而这两个参数恰好又是两个函数 , 只不过这两个函数不是我们定义的 , 我们在这里写的 resolve 和 reject 只是两个形参 , 你可以随便改名 , 叫啥都行

    在 promise 的构造器中会自动调用我们传入的这个函数 , 同时会将真正的实参函数 resolve 和 reject 传入进来

    而在我们的函数体中 , 当我们想要 履行承诺 时就调用 resolve 方法 , 并且将想要传递的值作为参数放入 ; 想要 拒绝承诺 时就调用 reject 方法 , 并且将拒绝的理由作为参数放入 ; 而无论是成功还是失败 , 我们放入的参数都会成为最终的 PromiseResult

    同时 , 刚刚才学过的 PromiseState 知识告诉我们 , 我们调用 resolve 方法后状态就会从 pending 变成 fulfilled , 调用 reject 方法后状态就会从 pending 变成 rejected

    你可以看到在我们上面的代码中 , x = 1 , 所以 x < 2 是成立的我们想要的结果 , 那么我们就 履行承诺 调用resolve 方法并且把 x 传入同时状态变成了 fulfilled , 否则呢我们就 拒绝承诺 调用 reject 方法并且把我们的拒绝理由 出错了 传入同时状态变成了 rejected

    总的来说就是 , 这两个参数函数和我们无关 , 我们只要达到目的的时候调用一下就可以了

    2.4 promise 的原型方法

    好了 , 基本上已经了解的差不多了 , 接下来我们说说原型上的方法

    2.4.1 then() 方法

    首先我们要先弄清楚什么叫履行承诺 , 什么叫拒绝承诺

    第一步是我给出承诺 , 如果我履行就干嘛干嘛 , 如果我拒绝就怎样怎样

    第二步是你看我的行为 , 我到底是履行了还是拒绝了 , 我履行了你就怎样怎样 , 我拒绝了你就干嘛干嘛

    其实就是一个根据上一个行为做出下一个行为

    我们来看以下代码

    let x = 1
    let p1 = new Promise((resolve, reject) => {
      if (x < 2) {
        resolve(x)
      } else {
        reject('出错了')
      }
    })
    p1.then(value => {
      console.log('value:' + value)
    }, reason => {
      console.log('reason:' + reason)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这段代码的使用过程中呢 , 我们可以看到 then() 方法接收两个参数 , 而第一个参数就是成功的回调 , 当一个 promise 使用 resolve 方法时 , then() 方法就会进入第一个参数函数 , 同时将 resolve 方法中的参数传入 ;
    当一个 promise 使用 reject 方法时 , then() 方法就会进入第二个参数函数 , 同时将 reject 方法中的参数传入

    上面的代码中 , 我们调用的是 resolve 方法 , 所以 x 作为参数传到了第一个函数中替代了 value 形参 , 所以就打印了 value: 1

    同样的如果将上面代码中的 x 修改为 10 , 就会调用 reject 方法 , 出错了 三个字就会作为参数传到第二个函数中替代 reason 形参 , 打印出 reason: 出错了

    这就是 then() 方法了 , 是不是又简单又好用

    2.4.2 catch() 方法

    我们已经了解到了简单好用的 then() 方法 , 但是我还是觉得一个参数中写两个函数很难受 , 别问问就是难受 , 那怎么办呢

    那当然就看看 catch() 方法了 , 我们来看以下代码

    let x = 10
    let p1 = new Promise((resolve, reject) => {
      if (x < 2) {
        resolve(x)
      } else {
        reject('出错了')
      }
    })
    p1.then(value => {
      console.log('value:' + value)
    }).catch(err => {
      console.log('err:' + err)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到 , 在 then() 中我只写了一个参数函数 , 那么按理说我们应该获取不到拒绝承诺的结果的 , 但是我们在后面又补上了一个 catch() 方法 , 同时 catch() 方法中的参数也是一个函数 , 所以 catch() 方法就是专门用来捕获拒绝的承诺 , 这样相比在 then() 中写两个函数要清晰明了很多 , 我们看看控制台中打印了什么

    在这里插入图片描述

    当然了 , 你可能会问 , 那我都写呗 , 当然没问题啦 , 只不过都写的话 , catch() 方法就会失效

    let x = 10
    let p1 = new Promise((resolve, reject) => {
      if (x < 2) {
        resolve(x)
      } else {
        reject('出错了')
      }
    })
    p1.then(value => {
      console.log('value:' + value)
    }, reason => {
      console.log('reason:' + reason)
    }).catch(err => {
      console.log('err:' + err)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    所以可以认为 catch() 方法是 then() 方法的语法糖 , 当正主出来的时候 , 替身自然就要下场了

    注意
    你可能已经注意到了 , 在上面的代码中 , 我们是在 then() 方法的末尾直接使用点语法调用的 catch() 方法 , 因此我们意识到每个方法都会返回一个新的 promise , 这样才能达到不断链式调用的效果

    2.4.3 finally() 方法

    顾名思义啊 , 这个 finally() 方法呢就是最后执行的方法 , 而且是无论如何都会执行的方法 , 不接收参数

    let x = 10
    let p1 = new Promise((resolve, reject) => {
      if (x < 2) {
        resolve(x)
      } else {
        reject('出错了')
      }
    })
    p1.then(value => {
      console.log('value:' + value)
    }).catch(err => {
      console.log('err:' + err)
    }).finally(a => {
      console.log('finally:', a)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    我们看到 finally 打印出来的 a 是 undefined , 所以 finally() 方法是没有参数的 , 而且一定会执行

    注意
    finally() 方法也会返回一个新的 promise , 所以同样可以链式调用

    p1.then(value => {
     console.log('value:' + value)
    }).finally(a => {
     console.log('finally:', a)
    }).catch(err => {
      console.log('err:' + err)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.5 Promise 的静态方法

    前面需要学习的原型方法就是那三个啦 , 接下来学习一下静态方法 , 也就是使用 static 声明在类中的方法

    2.5.1 Promise.resolve() 方法

    静态方法的话呢我觉得还是从 Promise.resolve() 方法开始学起最为简单 , 因为他的功能最简单

    效果 : 将参数包装为 promise 对象

    当然了 , 这里的参数需要分情况处理

    1. 参数为 promise 对象

      如果参数本身就已经是一个 promise 对象了 , 那就原封不动的返回这个 promise 对象

      let p1 = new Promise((resolve, reject) => { });
      let p2 = Promise.resolve(p1);
      console.log(p1 === p2); // true
      
      • 1
      • 2
      • 3
    2. 参数是普通数据

      参数是数字 , 字符串 , 布尔值 , undefined , null , 数组 , 对象 , Set , Map 时则返回一个新的Promise对象,且 PromiseState 状态为 fulfilled , PromiseResult 值为参数值

      console.log(Promise.resolve(123))
      console.log(Promise.resolve('shaoyahu'))
      console.log(Promise.resolve(false))
      console.log(Promise.resolve(undefined))
      console.log(Promise.resolve(null))
      console.log(Promise.resolve([1, 3]))
      console.log(Promise.resolve({ name: 'shaoyahu' }))
      console.log(Promise.resolve(new Set([1, 2])))
      console.log(Promise.resolve(new Map([[1, 1], [2, 2]])))
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      在这里插入图片描述

    3. 没有参数

      没有参数就直接返回一个空值的 promise

      let p1 = Promise.resolve()
      let p2 = Promise.resolve(undefined)
      console.log(p1)
      console.log(p2)
      console.log(p1 === p2)
      
      • 1
      • 2
      • 3
      • 4
      • 5

      在这里插入图片描述

    2.5.2 Promise.reject() 方法

    用法和 Promise.resolve() 方法一样 , 只不过返回值的状态为 rejected , 其他没有区别

    2.5.3 Promise.all() 方法

    效果 : 对多个原有的 promise 包装进一个新的 promise 中 , 而这个新的 promise 只有 所有的 promise 全部状态变为 fulfilled 或者 任意一个 promise 状态变为 rejected 时才会从 pending 状态变为相应的状态

    all() 方法接收的参数是一个数组 , 将所有的 promise 对象放在数组中 , 如果出现不是 promise 对象的参数 , 则默认使用 Promise.resolve() 方法进行包裹

    简单来说就是所有的 promise 成功就成功 , 有一个失败就失败 , 我们看以下代码

    let p1 = new Promise((resolve, reject) => {
      resolve('p1')
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p2')
      }, 2000);
    })
    let p3 = new Promise((resolve, reject) => {
      resolve('p3')
    })
    let p = Promise.all([p1, p2, p3, 123])
    console.log(p)
    p.then(value => {
      console.log(value)
      console.log(p)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    我们来看看结果 , 很显然 , 一开始 p 是处于 pending 状态的 , 等数组中所有的 promise 都返回成功之后才改变为 fulfilled 状态 , 并且返回值是每个 promise 返回值组成的数组

    在这里插入图片描述

    如果有一个 promise 失败了 , 那么返回的就会是 rejected 状态的 p , 同时返回值是失败的那个 promise 的失败原因

    2.5.4 Promise.race() 方法

    这个方法和 all() 方法其实差不多 , 只不过你看名字是 race , 竞速嘿嘿

    效果 : 将多个 promise 实例 , 包装成一个新的 promise 实例 , 一旦迭代器中的某个 promise 成功或者失败 , 那么新的 promise 就会立即成功或者失败

    简单来说就是只取返回最快的值,无论是成功还是失败 , 我们看以下代码

    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p1')
      }, 1500);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p2')
      }, 2000);
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p3')
      }, 1000);
    })
    let p = Promise.race([p1, p2, p3])
    console.log(p)
    p.then(value => {
      console.log(value)
      console.log(p)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们从代码中可以看出 , 最快的应该是 p3 , 所以结果肯定就是 p3 了

    在这里插入图片描述

    2.5.5 Promise.any() 方法

    效果 : 对多个原有的 promise 包装进一个新的 promise 中 , 原有的 promise 中一旦有一个成功了 , 就返回成功的那个 promise 值, 并且新的 promise 状态改为 fulfilled

    简单来说 , 就是 Promise.all() 方法的眼瞎版 , 不用看失败的只看成功的 , 是 Promise.race() 方法的偏心版 , 我只拿第一个 , 但是我只看成功的 , 我们看以下代码

    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p1')
      }, 1500);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p2')
      }, 2000);
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('p3')
      }, 1000);
    })
    let p = Promise.any([p1, p2, p3])
    console.log(p)
    p.then(value => {
      console.log(value)
      console.log(p)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    p3 是最快的 , 但是它是失败的 , 所以返回值是最快成功的 p1

    在这里插入图片描述

    2.5.6 Promise.allSettled() 方法

    效果 : 对多个原有的 promise 包装进一个新的 promise 中 , 当所有的 promise 状态都发生改变时 , 新的 promise 状态改为 fulfilled , 并且返回值是原有 promise 返回值组成的数组加工后的值

    简单来说 , 就是 Promise.all() 方法忽略了失败 , 将所有的结果都返回过来 , 我们看以下代码

    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p1')
      }, 1500);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('p2')
      }, 2000);
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('p3')
      }, 1000);
    })
    let p = Promise.allSettled([p1, p2, p3])
    console.log(p)
    p.then(value => {
      console.log(value)
      console.log(p)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们直接看返回数据 , 你一下就会懂了 , 很清晰的

    在这里插入图片描述

    3. 总结

    Promise 作为前端必须要学习的一个重要知识点 , 想必大家上面的内容其实都会吧 , 我真是班门弄斧啊 , 那么之后 , 我们来重写一个 promise 类试试看吧

    4. 结束

    学到这里 , 终于可以松一口气了 , 怎么样 , 是不是已经完全掌握了呢 ?
    其实还是挺有难度的 , 难度在真正用起来可能会比较绕 , 但是方法就这些 , 除非你只是在调用接口

    如果你跟着做遇到了什么问题无法实现 , 请在评论区或者私信告诉我 , 我发动网友给你解答

  • 相关阅读:
    【集训DAY N】简单数学【数学】
    【Verilog基础】10.偶分频和奇分频
    在Winform开发中,我们使用的几种下拉列表展示字典数据的方式
    java计算机毕业设计医院预约挂号系统源码+系统+mysql数据库+lw文档
    想要精通算法和SQL的成长之路 - 判断子序列问题
    HR SaaS告别蛮荒
    三翼鸟:产品不会变,场景实时变
    webGL技术开发的软件类型
    力扣刷题记录22.1-----541. 反转字符串 II
    TensorFlow开发者认证通过心得
  • 原文地址:https://blog.csdn.net/shaoyahu/article/details/134022156