JavaScript高级 Promise

69 阅读7分钟

1. 异步代码的困难

我们调用一个函数,这个函数中发送网络请求(我们可以用定时器来模拟),如果发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去,如果发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息

在promise之前我们是使用回调函数

// 1.设计这样的一个函数
function execCode(counter, successCallback, failureCallback) {
  // 异步任务
  setTimeout(() => {
    if (counter > 0) { // counter可以计算的情况 
      let total = 0
      for (let i = 0; i < counter; i++) {
        total += i
      }
      // 在某一个时刻只需要回调传入的函数
      successCallback(total)
    } else { // 失败情况, counter有问题
      failureCallback(`${counter}值有问题`)
    }
  }, 3000)
}

// 2.ES5之前,处理异步的代码都是这样封装
execCode(100, (value) => {
  console.log("本次执行成功了:", value)
}, (err) => {
  console.log("本次执行失败了:", err)
})

2. promise的作用

image.png

new Promise((resolve, reject) => {

})

所以上面的困难我们可以通过promise解决

function execCode(counter) {
    return new Promise((resolve, reject) => {
        // 异步任务
        setTimeout(() => {
            if (counter > 0) { // counter可以计算的情况
                let total = 0
                for (let i = 0; i < counter; i++) {
                    total += i
                }
                // 成功的回调
                resolve(total)
            } else { // 失败情况, counter有问题
                // 失败的回调
                reject(`${counter}有问题`)
            }
        }, 3000)
    })
}

// 执行一次
execCode(255).then(value => {
    console.log("成功:", value)
}).catch(err => {
    console.log("失败:", err)
})

3. promise基本使用

Executor是在创建Promise时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数(resolve,reject)

Promise对象里的异步操作执行时有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),而Promise对象状态的改变,只有两种可能:从pending变为fulfilled或者从pending变为rejected

1. Promise的状态锁死

在我们调用resolve的时候,如果resolve传入的值本身不是一个Promise,那么会将该Promise的状态变成fulfilled,在之后我们去调用reject时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变Promise状态,因为他的状态已经变为fulfilled)

这里需要注意:一旦状态被确定下来(resolve或reject方法的第一次调用),Promise的状态会被锁死,该Promise的状态是不可更改的

// 1.创建一个Promise对象
const promise = new Promise((resolve, reject) => {
  // 注意: Promise的状态一旦被确定下来, 就不会再更改, 也不能再执行某一个回调函数来改变状态
  // 1.待定状态 pending
  console.log("111111")
  console.log("222222")
  console.log("333333")

  // 2.兑现状态 fulfilled
  resolve()

  // 3.拒绝状态 rejected
  reject()
})

2. resolve不同值

◼ 情况一:如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数;

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

const p = new Promise((resolve) => {
  // setTimeout(resolve, 2000)
  setTimeout(() => {
    resolve("p的resolve")
  }, 2000)
})

const promise = new Promise((resolve, reject) => {
  // 如果resolve的值本身Promise对象, 那么当前的Promise的状态会有传入的Promise来决定
  resolve(p)
})

promise.then(res => {
  console.log("then中拿到结果:", res)
})

◼ 情况三:如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据then方法的结果来决定Promise的状态

const promise = new Promise((resolve, reject) => {
  resolve({
    name: "kobe",
    then: function(resolve) {
      resolve(11111)
    }
  })
})

promise.then(res => {
  console.log("then中拿到结果:", res)
})

3. then方法参数

const promise = new Promise((resolve, reject) => {
  resolve("success")
  // reject("failure")
})

// then参数的传递方法: 可以传递两个参数
 promise.then(res => {
   console.log("成功回调~", res)
 }, err => {
   console.log("失败回调~", err)
 })
 
// 但是推荐这种写法
promise.then(res => {

}).catch(err => {

})

4. then方法的多次调用

一个Promise的then方法是可以被多次调用的,每次调用我们都可以传入对应的fulfilled回调,当Promise的状态变成fulfilled的时候,这些then回调函数都会被执行

const promise = new Promise((resolve, reject) => {
    resolve("success")
})

promise.then(res => {
    console.log("成功回调~", res)
})
promise.then(res => {
    console.log("成功回调~", res)
})
promise.then(res => {
    console.log("成功回调~", res)
})
promise.then(res => {
    console.log("成功回调~", res)
})

5. then方法的返回值

then方法本身是有返回值的,它的返回值是一个Promise,所以我们可以进行链式调用


// Promise本身就是支持链式调用
// then方法是返回一个新的Promise, 链式中的then是在等待这个新的Promise有决议之后执行的
promise.then(res => {
    // then方法是返回一个新的Promise, 这个Promise的决议是等到then方法传入的回调函数有返回值时, 进行决议
    return "bbbbbbbb"
}).then(res => {
    // res => bbbbbbbb
}).then(res => {
    // res => undefined
    return new Promise((resolve, reject) => {
        resolve("aaaaaa")
    })
}).then(res => {
    // res => aaaaaa
})

6. catch方法多次调用

同理变化为rejected状态时,所有的catch回调都会执行

const promise = new Promise((resolve, reject) => {
  reject("failure")
})

promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})

但是需要注意的是,下面这种情况会报错,因为then方法中没有失败的回调

const promise = new Promise((resolve, reject) => {
  reject("failure")
})

promise.then(res => {

})

promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})

7.catch方法返回值

上catch方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法

const promise = new Promise((resolve, reject) => {
    // reject("error: aaaaa")
    resolve("aaaaaa")
})

// 1.catch方法也会返回一个新的Promise
promise.catch(err => {
    console.log("catch回调:", err)
    return "bbbbb"
}).then(res => {
    console.log("then第一个回调:", res)
    return "ccccc"
}).then(res => {
    console.log("then第二个回调:", res)
})

8. catch方法的执行时机

当promise变为rejected的时候,会跳过then方法,找到最近的catch方法去执行

promise.then(res => {
    console.log("then第一次回调:", res)
    // throw new Error("第二个Promise的异常error")
    return "bbbbbb"
}).then(res => {
    console.log("then第二次回调:", res)
    throw new Error("第三个Promise的异常error")
}).then(res => {
    console.log("then第三次回调:", res)
}).catch(err => {
    console.log("catch回调被执行:", err)
})

9. finally方法

示无论Promise对象无论变成fulfilled还是rejected状态,最终都会被执行的代码

const promise = new Promise((resolve, reject) => {
  // pending

  // fulfilled
  resolve("aaaa")

  // rejected
  // reject("bbbb")
})

promise.then(res => {
  console.log("then:", res)
  // foo()
}).catch(err => {
  console.log("catch:", err)
  // foo()
}).finally(() => {
  console.log("哈哈哈哈")
  console.log("呵呵呵呵")
})

5.Promise类方法

1. resolve

// 实例方法
// const promise = new Promise((resolve) => {
//   // 进行一系列的操作
//   resolve("result")
// })
// promise.catch

// 类方法
const studentList = []
const promise = Promise.resolve(studentList)

promise.then(res => {
  console.log("then结果:", res)
})
// 相当于
// new Promise((resolve) => {
//   resolve("Hello World")
// })

2.reject

// 类方法
const promise = Promise.reject("rejected error")
promise.catch(err => {
  console.log("err:", err)
})
// 相当于
// new Promise((_, reject) => {
//   reject("rejected error")
// })

3. all

它的作用是将多个Promise包裹在一起形成一个新的Promise,新的Promise状态由包裹的所有Promise共同决定

✓ 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;

✓ 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p1 resolve")
    reject("p1 reject error")
  }, 3000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p2 resolve")
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p3 resolve")
  }, 5000)
})

// all:全部/所有
Promise.all([p1, p2, p3]).then(res => {
  console.log("all promise res:", res)
}).catch(err => {
  console.log("all promise err:", err)
})

4. allSettled

该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是rejected时,才会有最终的状态;

并且这个Promise的结果一定是fulfilled的

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p1 resolve")
    reject("p1 reject error")
  }, 3000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p2 resolve")
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p3 resolve")
  }, 5000)
})

// 类方法: allSettled
Promise.allSettled([p1, p2, p3]).then(res => {
  console.log("all settled:", res)
})

5. race

表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p1 resolve")
    // reject("p1 reject error")
  }, 3000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p2 resolve")
    reject("p2 reject error")
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p3 resolve")
  }, 5000)
})


// 类方法: race方法
// 特点: 会等到一个Promise有结果(无论这个结果是fulfilled还是rejected)
Promise.race([p1, p2, p3]).then(res => {
  console.log("race promise:", res)
}).catch(err => {
  console.log("race promise err:", err)
})

6. any

any方法会等到一个fulfilled状态,才会决定新Promise的状态;

◼ 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;

◼ 如果所有的Promise都是reject的,那么会报一个AggregateError的错误

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p1 resolve")
    reject("p1 reject error")
  }, 3000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p2 resolve")
    reject("p2 reject error")
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p3 resolve")
    reject("p3 reject error")
  }, 5000)
})

// 类方法: any方法
Promise.any([p1, p2, p3]).then(res => {
  console.log("any promise res:", res)
}).catch(err => {
  console.log("any promise err:", err)
})