ES6 Promise

发布时间 2023-11-29 18:09:13作者: 我是小凳子

Promise

  1. 一个容器,一个保存着某个未来才会结束的事件的结果。

  2. 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。一旦状态改变,就不会再变

const p = new Promise((resolve, reject) => {
  resolve("ok")
  throw new Error("报错啦")
})
p.then(res => console.log("then res:", res)).catch(err => console.log("catch err:", err))

// then res: ok
  • 状态的变化有两种:1. pending -> fulfilled 2. pending -> rejected

  • 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

  • 结果是不会改变的,不会同时走 resolve() 和 reject() 的!

  1. 调用 resolve 或 reject 不会终结 Promise 的参数函数的执行。
new Promise(function(resolve, reject) {
  resolve("resolve")
  console.log(1)
}).then(r => {
  console.log(r)
})

//  1
// resolve

立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。

一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。

  • resolve() 的作用:将状态 pending -> fulfilled , 在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

  • reject() 的作用:将状态 pending -> rejected , ,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

  1. “Promise 会吃掉错误”:Promise 内部的错误不会影响到 Promise 外部的代码(Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。)。
const fn = function() {
  return new Promise((resolve, reject) => {
    resolve(x+1)
  })
}

fn().then(res => console.log("res:", res)) // 没有捕获处理错误的噢!

setTimeout(() => console.log("继续"), 3000)
// 报错 Uncaught (in promise) ReferenceError: x is not defined
// 继续

抛出错误,3秒后,打印“继续”

Promise.prototype.then() 分别指定 resolved 状态和 rejected 状态的回调函数

then() 是定义在原型对象 Promise.prototype 上的,返回一个新的 Promise 实例,为 Promise 实例添加状态改变时的回调函数。可以采用链式写法。

  1. 两个参数:第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数,它们都是可选的。
new Promise((resolve, reject) => {
  reject('出错啦')
}).then(res => console.log("res", res), err => console.log("err", err))
  1. 链式写法,回调函数依次被调用。

Promise.prototype.catch() 用于指定发生错误时的回调函数

catch() 是 then(null, reject) 或 then(undefined, reject) 的别名。catch() 返回一个 Promise 对象

new Promise((resolve, reject) => {
  reject(21)
}).then(res => console.log("res:", res)).catch(err => console.log("catch err:", err)) 
// catch err: 21

// 等同于:
new Promise((resolve, reject) => {
  reject(21)
}).then(res => console.log("res:", res)).then(null, err => console.log("then err:", err))
// then err: 21
  1. 如果异步操作抛出错误,状态会变成 rejected , 就会调用 catch() 指定的回调函数。
const p = new Promise((resolve, reject) => {
  throw new Error('出错啦')
})

p.catch(err => console.log(err))

以上代码等同于以下的两种写法:

// 写法一:
const p = new Promise((resolve, reject) => {
  try {
    throw new Error("出错啦")
  }catch(e) {
    reject(e)
  }
})

p.catch(err => console.log(err))
// 写法二:
const p = new Promise((resolve, reject) => {
  reject(new Error("出错啦"))
})

p.catch(err => console.log(err))

reject() 的作用:抛出错误。

  • then() 指定的回调函数如果运行中抛出错误,也会被 catch() 捕获到。
  1. Promise 对象的错误具有“冒泡”的性质,错误与会一直往后传递,直至被捕获

  2. new Promise(...).catch(...).then(...) :

catch() 只会捕获到前一个 Promise 抛出的错误,没有错误的话会跳过 catch() 继续执行后边的 then() 。如果在后边的 then() 中抛出了错误,这个错误无法捕获到(与 then() 前边的 catch() 无关了),也不会传递到外层。

const p = new Promise((resolve, reject) => {
  reject(new Error('出错啦'))
})

p.catch(err => console.log("err:", err)).then(res => console.log("状态:resolved"), err => console.log("状态:rejected"))

Promise.prototype.finally() 总会执行指定的回调函数(不论 Promise 的最终状态)

new Promise((resolve, reject) => {}).finally(() => {
  console.log("一直存在")
})

// 等同于
new Promise((resolve, reject) => {}).then(res => {
  console.log("一直存在")
  return res
}, err => {
  console.log("一直存在")
  throw err
})
  1. 回调函数不接受任何参数

  2. finally() 总是会返回原来的值

// 实现:
Promise.prototype.finally = function(callback) {
  let p = this.constructor;
  return this.then(
    value => p.resolve(callback()).then(() => value),
    reason = p.resolve(callback()).then(()=> {throw reason})
    )
}
// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

Promise.resolve() 将现有对象转为 Promise 对象

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  1. 参数是一个 Promise 实例,Promise.resolve() 原封不动返回这个实例。
const p = new Promise(resolve => resolve('foo'))
const p2 = Promise.resolve(p)
p === p2 // true
  1. 参数是一个 thenable 对象(具有 then 方法的对象),Promise.resolve() 会将这个对象转为 Promise 对象,然后立即执行这个对象的 then() 方法。
const thenable = {
  then: (resolve, reject) => {
    resolve(42)
  }
}
const p = Promise.resolve(thenable)
p.then((res) => console.log(res)) // 42
  1. 参数不是 thenable 对象或者不是一个对象(如 42,'foo'等),Promise.resolve() 会返回一个新的 Promise 对象,状态是 resolved 。
const p = Promise.resolve('foo')
p.then(res => console.log('res', res), err => console.log('err', err)) // res foo
  1. 不带参数,Promise.resolve() 直接返回一个 resolved 状态的 Promise 对象。

立即resolve()的 Promise 对象,是在本轮“事件循环”的结束时执行,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

setTimeout(fn, 0)下一轮“事件循环”开始时执行。

Promise.reject() 返回一个新的状态为 rejected 的 Promise 实例

  1. 返回的是一个的 Promise 实例。

  2. 参数原封不动地作为reject的理由,变成后续方法的参数。如:

Promise.reject('出错啦').catch(e => console.log('e', e)) // e 出错啦

Promise.all() 将多个 Promise 实例包装成一个新的 Promise 实例

只适用所有异步操作成功的情况,如果有一个异步操作失败,都无法进行下一步操作。

异步操作成功,是不会抛出错误的意思,并非是 状态 rejected 。

const p = Promise.all([p1, p2, p3]);
  1. 接受一个数组作为参数,数组成员都是 Promise 实例。如果成员不是 Promise 实例,会先调用 Promise.resolve() 将其转为 Promise 实例。

参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

  1. p 的状态由 p1 、p2 、p3 决定
  • 只有 p1 、p2 、p3 的状态都变成 fulfilled , p 的状态才会变成 fulfilled , 此时 p1 、p2 、p3 的返回值组成一个数组传递给 p 的回调函数。

  • 只要 p1 、p2 、p3 任意一个被 rejected , p 的状态就是 rejected , 此时第一个被 reject 的实例的返回值会传递给 p 的回调函数。

总结:只有 p1 、p2 、p3 的状态有被改变,才会调用 Promise.all 方法后面的回调函数。

  1. 如果作为参数的 Promise 实例,自己定义了 catch 方法,那么它一旦被 rejected , 并不会触发 Promise.all() 的 catch 方法。
// p1 resolved
const p1 = new Promise((resolve, reject) => resolve('hi')).then(res => {
  console.log("p1 res :", res)
  return res
}).catch(err => {
  console.log("p1 err :", err)
  return err
})

// p2 先 rejectd -> p2 有 catch() , 执行后,返回一个新的 Promise 实例, p2 指向了这个新实例,状态为 resolved
// rejected : catch -> 抛出错误:报错啦 => 返回一个新的 Promise 对象  状态: resolved
const p2 = new Promise((resolve, reject) => {throw new Error('报错啦')}).then(res => {
  console.log("p2 res :", res)
  return res
}).catch(err => {
  console.log("p2 err :", err)
  return err
})

// resolved
Promise.all([p1, p2]).then(res => console.log('Promise.all res :', res)).catch(err => console.log('Promise.all err :', err))

// p1 res : hi
// p2 err : [Error: 报错啦]
// Promise.all res : [ 'hi', [Error: 报错啦] ] 

如果 p2 没有 catch 方法,就会调用 Promise.all() 的 catch 方法,输出结果就不同了:

const p2 = new Promise((resolve, reject) => {throw new Error('报错啦')}).then(res => {
  console.log("p2 res :", res)
  return res
})

// p1 res : hi
// Promise.all err : [Error: 报错啦]

Promise.race() 将多个 Promise 实例包装成一个新的 Promise 实例

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

参数同 Promise.all() , 与其不同点是 p 状态的改变:

  1. 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例(其实就是第一个 Promise 改变状态)的返回值,就传递给p的回调函数。

  2. Promise.all() 和 Promise.race() 传入空数组[] 作为参数:

  • Promise.all() 传入一个空数组,会立即完成

  • Promise.race() 传入一个空数组,会被挂住,且不会决议(状态:pending)。

总结:p1、p2、p3 任意一个实例状态改变,p 的状态就跟着变了。

Promise.allSettled() 确定一组异步操作是否都结束了(不论操作成功或失败)

const p = Promise.allSettled([p1, p2, p3]);
  1. Promise.allSettled() 接受一个数组作为参数,数组每个成员皆为 Promise 对象。返回一个新的 Promise 对象。

  2. 状态变化:只有 p1, p2, p3 所有 Promise 对象都发生状态变更(不论是 fulfilled / rejected),p (返回的 Promise 对象)才会发生状态变更

  3. Promise.allSettled() 返回的新的 Promise 实例,一旦状态发生变更,状态总是 fulfilled ,不会是 rejected 的。

  • 状态变为 fulfilled 后,它的回调函数会接收一个数组作为参数,数组成员对应前面数组的每一个 Promise 对象,对象的格式是固定的:
const resolved = Promise.resolve(42)
const rejected = Promise.reject(-1)
const p = Promise.allSettled([resolved, rejected])

p.then(res => console.log(res))
// [ { status: 'fulfilled', value: 42 }, { status: 'rejected', reason: -1 } ]

Promise.any() 将多个 Promise 实例包装成一个新的 Promise 实例

const p = Promise.any([p1, p2, p3]);
  1. Promise.any() 接受一个数组作为参数,数组每个成员皆为 Promise 对象。返回一个新的 Promise 对象。

  2. p 状态改变

  • p1, p2, p3 只要有一个状态变为 fulfilled , p 的状态:fulfilled

  • p1, p2, p3 都变成 rejected , p 的状态:rejected

const resolved = Promise.resolve(21)
const rejected = Promise.reject(-1)

Promise.any([resolved, rejected]).then(res => console.log("res:", res), err => console.log("err:", err))
// res: 21
  1. 与 Promise.race() 不同处在于某一个参数 Promise 的状态变为了 rejected 的继续操作:
  • Promise.race() 某一个参数 Promise 变成了 rejected 就结束了。

  • Promise.any() 必须等到所有参数 Promise 变成 rejected 状态才会结束。

Promise.any() 参数 Promise 但凡有一个状态变为了 fulfilled ,都不会被 catch() 捕获到。