深入理解 JS 异步编程 01(Promise 篇)

Promise 详解及应用

Promise 概述与初体验

Promise: 一个类是异步编程的一种解决方案,当我们需要给予调用者一个承诺(异步处理),等待异步处理结束后在给开发者回调数据时,就可以创建一个 Promise 对象来进行处理

  • 在通过 new 构造 Promise 实例对象时,需要传入一个回调函数(称之为 executor 执行器)

  • executor: 该执行器回调函数会立即执行,并会给该回调传入另外两个回调函数 "resolve"、"reject"

    • resolve:
      • 当执行该回调函数时,会执行 Promise 实例对象中的 then 方法传入的回调函数
      • 执行该函数时,可通过传入对应的数据,该数据会传入对象上 then 方法回调函数中
    • reject: 当执行该回调函数时,会执行 Promise 实例对象中的 catch 方法传入的回调函数或执行 then 方法中的第二个回调
      • 执行该函数时,可通过传入对应的数据,该数据会传入对象上 catch 方法回调函数中
  • 示例 ↓

    • // -- 1. 创建 Promise 实例对象 → 需要传入一个 executor 执行器函数
      const promise = new Promise((resolve, reject) => { // -- 2. executor 函数中可以接收 resolve 和 reject 两个回调函数
          resolve("成功") // -- 3. 当调用 resolve 回调时,就会执行该 Promise 实例对象上的 then 方法中的回调函数 → 并将执行回调函数的参数传递给 then 方法的回调函数中
      })
      
      promise.then(res => { // -- 4. 当在 executor 中执行力 resolve 方法时,就会执行该 then 中的回调函数
          console.log(res) // log: 成功
      }).catch(reason => { // -- 5. 当在 executor 中执行了 reject 方法时,就会执行该 catch 中的回调函数
          console.log(reason);
      })
      
    • const promise = new Promise((resolve, reject) => {
          setTimeout(() => { // -- 模拟异步处理
              resolve("成功")
          }, 2000)
      })
      
      promise.then(res => {
          console.log(res) // 2s 后将打印: 成功
      })
      

Promise 的三中状态

  • Pending: 等待态 → 初始状态,既没有被兑现,也没有被拒绝

    • tip:可以迁移至执行态或拒绝态
  • Fulfilled: 成功态 → 当执行了 executor 中的 resolve 回调时,表示 promise 已被兑现,将执行对应 then 方法中的回调函数

    • tip:不能迁移至其他任何状态
  • Rejected: 拒绝态 → 当执行了 executor 中的 reject 回调时,表示 promise 已被拒绝,将执行对应 catch 方法或 then 方法中的第二个回调函数

    • tip:不能迁移至其他任何状态
  • tip: 注意,在 Promise 的 executor 中状态只能修改一次,状态一旦确定下来后,将不可再进行修改 如 ↓

    • const promise = new Promise((resolve, reject) => {
          resolve("成功") // -- resolve → Fulfilled 状态 → 不可再修改状态
          reject("失败") // -- 🔺因为上面已经执行了 executor 中的 resolve 方法,所以状态已确定为了 "Fulfilled" 了,再执行 reject 时不会再改变对应的状态 → 即相当于改行代码什么都没有做(无意义代码)
      })	
      
      promise.then(res => {
          console.log(res) // log: 成功
      }).catch(reason => {
          console.log(reason);
      })
      

resolve 传递的值的不同情况

resolve 函数非常灵活,可以处理各种类型的值,这使得Promise在异步编程中变得非常强大和灵活,通常可以分为如下三种不同的情况

情况一: 如果 resolve 传入的是一个普通的值或对象时,该值会作为 then 回调函数的参数

  • const promise = new Promise((resolve, reject) => {
        resolve("成功") // -- 传入一个普通值
    })
    
    promise.then(res => {
        console.log(res) // log: 成功
    })
    

情况二: 如果 resolve 传入的是另一个 Promise,那么该新的 Promise 的状态会决定原来 Promise 的状态

  • const promise = new Promise((resolve, reject) => {
        // -- 传入一个 Promise 对象,当前 Promise 的状态将,将由该传入的 Promise 的状态决定
        resolve(
            new Promise((resolve, reject) => {
                reject("失败")
            })
        )
    })
    
    promise.then(res => {
        console.log(res)
    }).catch(err => {
        console.log(err) // log: 失败
    })
    

情况三: 传入一个 thenable 对象(一个定义了 “.then” 方法的对象或函数,即有 then 方法的对象就可以称之为时一个 thenable 对象)

  • 如果 resolve 传入的是一个带有 then 方法的 thenable 对象时,那么会执行该对象中的 then 方法,且根据 then 方法的结果来决定 Promise 的状态

  • 该 then 方法执行时,也可以接收 resolve 与 reject 两个回调函数,与 executor 同理,可以通过该两个函数来决定当前 Promise 的状态即对应的结果值

  • const promise = new Promise((resolve, reject) => {
        // -- 传入一个对象,且对象中有对应 then 方法的实现 → 会先执行该对象中的 then 方法,且方法中接收 resolve 与 reject 两个回调函数
        // -- 当前 Promise 的状态将有该 then 方法中的行为决定
        resolve(
            {
                then(resolve, reject) { // -- 在 then 方法中调用 reject 方法,将使当前 Promise 对象的状态变为 Rejected
                    reject("失败")
                }
            }
        )
    })
    
    promise.then(res => {
        console.log(res)
    }).catch(err => {
        console.log(err) // log: 失败
    })
    

Promise 实例方法

then

then 方法是 Promise 对象上的一个实例方法

then 方法接收两个参数: 在 then 方法中可以接收两个回调函数 onFulfilled 与 onRejected(可选)

  • onFulfilled: 当 Promise 的状态为 Fulfilled(成功)时被调用,该函数接收一个成功的结果值

  • onRejected: 当 Promise 的状态为 Rejected(失败)时被调用,该函数接收一个失败的原因值(错误捕获)

  • const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("成功")
            // 或者: reject("失败")
        }, 1000)
    })
    
    promise.then(
        res => {
            console.log(res) // log: 成功
        },
        reason => {
            console.log(reason) // 或者: log: 失败
        }
    )
    

**then 方法的多次调用: **

  • promise 对象的 then 方法允许被调用多次

  • 当 promise 的状态发生改变时,通常会按照多次调用 then 方法的顺序来执行对应多有 then 的回调函数

  • const promise = new Promise((resolve, reject) => {
        resolve("成功")
    })
    
    // -- 多次调用 then 方法
    promise.then(res => {
        console.log(res + 1) // log: 成功1
    })
    promise.then(res => {
        console.log(res + 2) // log: 成功2
    })
    promise.then(res => {
        console.log(res + 3) // log: 成功3
    })
    

then 方法的返回值:

  • then 返回会返回一个新的 Promise 实例(then 函数在内部会自动给返回值一层 Promise.resolve(returnValue) 的包装,包装成一个 Promise)

  • then 的返回值 returnValue 与上面 resolve 中的一样,同样分为对应的三种情况(具体如上面的 resolve 处)

  • 这就意味着 .then 方法可以被链式调用,每个 .then() 调用都会基于前一个 Promise 的结果

  • const promise = new Promise((resolve, reject) => {
        resolve("成功")
    })
    
    // -- then 链式调用及其不同返回值的情况示例
    promise.then(res => {
        console.log(res) // log: 成功
        return "then 成功"
    }).then(res => {
        console.log(res) // log: then 成功
        return new Promise((resolve, reject) => {
            resolve("new promise 成功")
        })
    }).then(res => {
        console.log(res) // log: new promise 成功
        return {
            then(resolve, reject) {
                reject("thenable object call reject 失败")
            }
        }
    }).then(res => {
        console.log(res) // -- 不会进入该回调,将执行 then 的第二个回调函数 onRejected 处
    }, err => {
        console.log(err) // log: thenable object call reject 失败
    })
    

catch

catch 方法也是 Promise 对象上的一个实例方法

当 promise 对象上的状态变成 rejected(失败态)是就会触发 catch 种的回调(捕获错误)

  • const promise = new Promise((resolve, reject) => {
        reject("失败")
    })
    
    promise.then(res => {
        console.log(res)
    }).catch(err => {
        console.log(err) // log: 失败
    })
    
  • + 注意: 当执行的 reject 方法已经在 then 种的第二个回调函数种进行捕获后,将不会再进入 catch 方法(至于为什么? → 如下面的 catch 返回值处)
    promise.then(res => {
        console.log(res)
    }, err => {
        console.log(err + " in then") // log: 失败 in then
    }).catch(err => {
        console.log(err) // 失败状态已经再上面的 then 种的 onRejected 回调函数种捕获,所以不会进入该 catch 方法
    })
    

catch 的返回值:

  • 与 then 方法的返回值一样通过会返回一个新的 promise 对象(通过也会经过 Promise.resolve 方法进行包装成 promise 对象)
  • 所以 catch 或 then 种的 onRejected 回调再捕获后,返回的 promise 默认也是 fulfilled 状态的
  • 当然该方法的返回值,也有分为前面的三种情况(具体看上面)

finally

finally 是在 ES9 中新增的一个特性: 表示 promise 对象无论变成了 fulfilled 状态函数 rejected 状态,最终都会执行该方法上的回调函数(该方法不接受参数)

  • const promise = new Promise((resolve, reject) => {
        resolve("成功")
    })
    
    promise.then(res => {
        console.log(res) // log: 成功
    }).catch(err => {
        console.log(err)
    }).finally(() => {
        console.log("finally action") // log: finally action
    })
    

Promise 静态方法

resolve

  • 该方法相当于是 new Promise,并执行了 executor 中的 resolve 方法(通常我们有一个现成的内容,希望转换成 Promise 来使用时,就可以调用该方法,如 then 的内部就会将对应的返回值包装成一个 Promise 对象使用)

    • Promise.resolve("kong").then(res => {
          console.log(res) // log: kong
      })
      
      // -- 等价于
      
      new Promise((resolve, reject) => {
          resolve('kong')
      }).then(res => {
          console.log(res) // log: kong
      })
      
  • tip: Promise.resolve(参数) 中的 "参数" 值,与前面 resolve 传值的一样,也分为对应的集中几种情况(具体如上面)

reject

  • 该方法相当于是 new Promise,并执行了 executor 中的 reject 方法

    • Promise.reject("失败").catch(reason => {
          console.log(reason) // log: 失败
      })
      
      // -- 等价于
      
      new Promise((resolve, reject) => {
          reject("失败")
      }).catch(reason => {
          console.log(reason)
      })
      
  • tip: 需要注意 reject 方法传入的值,没有像 resolve 那样有不同的情况 → 所以无论 reject 传入的值是什么形态的,都会 "直接" 进入 catch 捕获中

    • +: reject 传入的是另一个成功的 promise 对象 → 但不会影响当前 promise 对象的结果,会直接进入 catch 捕获中
      Promise.reject(
          Promise.resolve("kong") // -- 1. 传入的是另一个 promise 对象
      ).then(res => { // -- 2. 不会进入该 then 方法,因为 reject 方法不会因为参数的形态来改变当前 promise 的状态(会直接进入 catch 捕获中)
          console.log("成功:" + res)
      }).catch(err => {
          console.log("失败:" + err) // 🔺log: 失败:[object Promise]
      })
      

all

  • 该方法接收一个 promise 对象的可迭代对象(如数组) 作为参数,返回一个新的 Promise 实例对象(即: 将多个 Promise 包裹在一起,形成一个新的 Promise)

  • 所返回的 Promise 的状态

    • fulfilled: 当所有传入的 Promise 对象都成功完成时才会成功完成,其中结果就包含所有成功结果的值的数组(顺序与输入的可迭代对象中的顺序相同)

      • const p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve("p1 成功")
            }, 1000)
        })
        
        const p2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve("p2 成功")
            }, 2000)
        })
        
        const p3 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve("p3 成功")
            }, 3000)
        })
        
      • + 🔺
        Promise.all([p1, p2, p3]).then(res => {
            console.log(res) // log: [ 'p1 成功', 'p2 成功', 'p3 成功' ]
        })
        
    • rejected: 当所有传入的 Promise 对象中的任意一个失败时,则该新的 promise 会立即以那个失败的 Promise 的结果为失败结果

      • const p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve("p1 成功")
            }, 1000)
        })
        
        const p2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject("p2 失败")
            }, 3000)
        })
        
        const p3 = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject("p3 失败")
            }, 2000) // -- 该 p3 会先失败
        })
        
      • + 🔺
        Promise.all([p1, p2, p3]).then(res => {
            console.log(res)
        }).catch(reason => {
            console.log(reason) // log: p3 失败
        })
        

allSettled(ES11)

  • 该方法接收一个 promise 对象的可迭代对象作为参数,返回一个新的 Promise 实例对象

  • all 方法的缺陷

    • 当有其中一个 Promise变成 reject 状态时,新 Promise 就会立即变成对应的 reject 状态
    • 那么对于 resolved 以及依然处于 pending 状态的 Promise,我们是获取不到对应的结果的
    • 而该 API 就可以解决 all 中的缺陷
  • 特点

    • 该方法返回的新的 Promise 实例对象,会在所有传入的 Promise 对象都完成时(无论失败或成功),就会进入 then 方法中(即: 该方法返回的 Promise 的状态结果一定时 fulfilled)
    • 其返回的结果为一个数组,数组存放着每一个 promise 对象的结果,每个对象中都包含 status 与 value/reason 属性
      • status 属性: 表示该 promise 对象的状态,"fulfilled" 或 "rejected"
      • value 属性: 当 status 为 "fulfilled" 成功时,才会存在该属性,值为成功的结果值
      • reason 属性: 当 status 为 "rejected" 失败时,才会存在该属性,值为失败的原因值
  • 示例

    • const p1 = new Promise((resolve, reject) => {
          setTimeout(() => {
              resolve("p1 成功")
          }, 1000)
      })
      
      const p2 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p2 失败")
          }, 3000)
      })
      
      const p3 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p3 失败")
          }, 2000)
      })
      
    • + 🔺
      Promise.allSettled([p1, p2, p3]).then(res => {
          /** log: ↓
              [
                  { status: 'fulfilled', value: 'p1 成功' },
                  { status: 'rejected', reason: 'p2 失败' },
                  { status: 'rejected', reason: 'p3 失败' }
              ]
           */
          console.log(res)
      }).catch(reason => {
          console.log(reason) // -- allSettled 不会进入 catch 中,因为 allSettled 返回的 Promise 的状态一定是 fulfilled 
      })
      

race

  • 该方法接收一个 promise 对象的可迭代对象作为参数,返回一个新的 Promise 实例对象

  • 该返回的 Promise 的状态,由传入的多个 promise 中最先完成的决定(无论成功或失败),即谁先有结果,那么就使用谁的结果

    • const p1 = new Promise((resolve, reject) => {
          setTimeout(() => {
              resolve("p1 成功")
          }, 2000)
      })
      
      const p2 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p2 失败")
          }, 1000)
      })
      
      const p3 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p3 失败")
          }, 2000)
      })
      
    • + 🔺
      Promise.race([p1, p2, p3]).then(res => {
          console.log(res)
      }).catch(err => {
          console.log(err) // log: p2 失败
      })
      

any(ES12)

  • 该方法接收一个 promise 对象的可迭代对象作为参数,返回一个新的 Promise 实例对象

  • 该返回的 Promise 对象的状态,只关心所传入的多个 Promise 对象是否有 Promise 解决,而不关心解决的是哪一个(即会等待一个 promise 对象为 fulfilled 状态,才会决定该新 Promise 对象的状态)

    • const p1 = new Promise((resolve, reject) => {
          setTimeout(() => {
              resolve("p1 成功")
          }, 3000)
      })
      
      const p2 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p2 失败")
          }, 1000)
      })
      
      const p3 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p3 失败")
          }, 2000)
      })
      
    • + 🔺
      Promise.any([p1, p2, p3]).then(res => {
          // -- 会等待第一个 fulfilled 状态 → 即 3s 后的 p1 的结果
          console.log(res) // log: p1 成功
      }).catch(err => {
          console.log(err)
      })
      
  • 如果所有的 Promise 都拒绝(rejected),那么返回的 Promise 将以一个 AggregateError 实例为原因拒绝,这个 AggregateError 实例包含所有失败的 Promise 的原因

    • tip: 通过 AggregateError 实例上的 errors 属性获取所有失败的原因

    • const p1 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p1 失败")
          }, 2000)
      })
      
      const p2 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p2 失败")
          }, 1000)
      })
      
      const p3 = new Promise((resolve, reject) => {
          setTimeout(() => {
              reject("p3 失败")
          }, 2000)
      })
      
    • + 🔺
      Promise.any([p1, p2, p3]).then(res => {
          console.log(res)
      }).catch(reason => {
          console.log(reason) // log: AggregateError: All promises were rejected
          console.log(reason.errors) // log: [ 'p1 失败', 'p2 失败', 'p3 失败' ]
      })	
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值