Skip to content
本页目录

Promise

优缺点

优点:

  • 解决回调嵌套问题(回调地狱)

缺点:

  • 无法取消:创建了就会立即执行,无法中途取消
  • 如果不设置回调函数,内部抛出的错误外部无法知道
  • 当处于 pending 状态时,无法知道进度

构造函数

Promise 对象本身就是个构造函数

三种状态

三种状态,确定了就无法改变

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

参数

  • Promise 接受一个 function 作为参数
  • resolvereject 是接受的 function 的参数,且这个参数也都是 function

实例方法

then()

Promise 实例添加状态改变时的回调函数

JS
// res 对应 resolve 成功回调
// rej 对应 reject 失败回调
// then 返回一个新的 promise 实例
.then((res, rej) => {

})
// res 对应 resolve 成功回调
// rej 对应 reject 失败回调
// then 返回一个新的 promise 实例
.then((res, rej) => {

})

catch()

Promise.prototype.catch() 方法是 .then(null, rejection).then(undefined, rejection) 的别名,用于指定发生错误时的回调函数。

JS
.catch(err => {

})
.catch(err => {

})

实现

JS
Promise.prototype.catch() = function(fn) {
    return this.then(null, fn);
}
Promise.prototype.catch() = function(fn) {
    return this.then(null, fn);
}

finally()

无参数,无论结果状态如何都会执行,可用于接口调用完成后关闭 loading 状态等
实现:

JS
Promise.prototype.finally = function (cb) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(cb()).then(() => value),
    reason => P.resolve(cb()).then(() => { throw reason })
  );
};
Promise.prototype.finally = function (cb) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(cb()).then(() => value),
    reason => P.resolve(cb()).then(() => { throw reason })
  );
};

概括

方法.then.catch说明
all()所有都成功时 resolve 的结果数组第一个 reject 结果只要有一个失败就会终止,不会再走 .then()方法
allSettled()所有结果的对象数组-会执行所有 proimise
any()第一个 resolve 的值所有都失败 AggregateError 类数组对象遇到第一个成功就 .then(),全部失败走 .catch()
race()第一个 resolve 的值第一个 reject遇到第一个成功会失败就会终止

all()

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

  • 只有三个都为 resolve 状态的时候才会调用 .then 方法
  • 只要有一个 promise 的状态为 rejected,就会调用 .catch 方法,而不管其它的请求是否结束。

Promise.all 获得的成功结果的数组里面的数据顺序和 Promise.all 接收到的数组顺序是一致的,即 p1 的结果在前,即便 p1 的结果获取的比 p2 要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用 Promise.all 毫无疑问可以解决这个问题。
白话理解:Promise.all 所有异步会几乎同时发起,假设都成功,它才会进入 .then() 方法,所以此时能保证接口都请求完成了。

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

p 的状态由 p1、p2、p3 决定,分成两种情况:

  1. 只有 p1、p2、p3 的状态都变成 fulfilledp 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数。
  2. 只要 p1、p2、p3 之中有一个被 rejectedp 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。
JS
Promise.all([promise1, promise2, promise3])
.then(res => {
    console.log(res)    // res 是一个对应 resolve 的数组,可以使用解构方法处理对应的结果
    
})
.catch(err => console.log(err))
Promise.all([promise1, promise2, promise3])
.then(res => {
    console.log(res)    // res 是一个对应 resolve 的数组,可以使用解构方法处理对应的结果
    
})
.catch(err => console.log(err))

实现一个 Promise.all() ES2020

JS
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    // 参数可以不是数组,但必须具有 Iterator 接口
    if (typeof promises[Symbol.iterator] !== "function") {
      reject("Type error");
    }

    if (promises.length === 0) {
      resolve([]);
    } else {
      const res = [];
      let count = 0;
      const len = promises.length;
      for (let i = 0; i < len; i++) {
        //考虑到 promises[i] 可能是 thenable 对象也可能是普通值
        Promise.resolve(promises[i])
          .then((data) => {
            res[i] = data;
            if (++count === len) {
              resolve(res);
            }
          })
          .catch((err) => {
            reject(err);
          });
      }
    }
  });
};
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    // 参数可以不是数组,但必须具有 Iterator 接口
    if (typeof promises[Symbol.iterator] !== "function") {
      reject("Type error");
    }

    if (promises.length === 0) {
      resolve([]);
    } else {
      const res = [];
      let count = 0;
      const len = promises.length;
      for (let i = 0; i < len; i++) {
        //考虑到 promises[i] 可能是 thenable 对象也可能是普通值
        Promise.resolve(promises[i])
          .then((data) => {
            res[i] = data;
            if (++count === len) {
              resolve(res);
            }
          })
          .catch((err) => {
            reject(err);
          });
      }
    }
  });
};

allSettled()

参数数组的所有 Promise 对象都发生状态变更(不管是 fulfilled 还是 rejected ),返回的 Promise 对象才会发生状态变更。 解决 all() 方法异常会阻断其它请求的问题。

白话解释:不管数组中每个执行成功还是失败,都会在 .then() 中返回对应状态与结果。

any() ES2021

只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态。 大白话:除非所有实例都失败(rejected)时才进入 .catch(),否则只要有成功就会终止并进入 .then()

注意:这里返回是无序的,第一个指第一个返回,而不是数组中第一个执行的 promise

race()

any() 相似,区别是只要有一个 Promise 状态变为 rejected,就会结束,所以 .then() 方法只会返回最早 resolve() 的结果,而 any() 是要所有参数 Promise 变成 rejected 状态才会结束 白话解释:第一个返回的是成功就 .then(),失败则 .catch()
注意:这里返回是无序的,第一个指第一个返回,而不是数组中第一个执行的 promise

Promise中的 then 第二个参数和 catch 有什么区别?

  • 如果在 then 的第一个函数里抛出了异常,后面的 catch 能捕获到,而 then 的第二个函数无法捕获到。
  • 如果是 Promise 内部报错,reject 抛出错误后,then 的第二个参数和 catch 方法都存在的情况下,只有 then 的第二个参数能捕获到,如果 then 的第二个参数不存在,则 catch 方法会捕获到。

所以在日常代码中,应该使用 .catch 方式去捕获异常。

catch 只是一个语法糖,最终还是通过 then 来处理:

JS
Promise.prototype.catch() = function(fn) {
    return this.then(null, fn);
}
Promise.prototype.catch() = function(fn) {
    return this.then(null, fn);
}

async 与 await

  • 执行 async 函数会返回一个 Promise 对象
  • await 相当于 Promisethen
  • try...catch 相当于 Promisecatch

测试代码

JS
let p1 = new Promise((resolve, reject) => {
    resolve({ id: 1 })
})

let p2 = new Promise((resolve, reject) => {
    reject({ id: 2 })
})

let p3 = new Promise((resolve, reject) => {
    resolve({ id: 3 })
})

 Promise.all([p1, p2, p3])
.then(res => {
    console.log(res)
}).catch((err) => {
    // 有一个 reject 就会进入
    console.log(err) // {id: 2}
})


Promise.allSettled([p1, p2, p3])
.then(res => {
    console.log(res) // [{status: "fulfilled", value: { id: 1 }}, {status: "rejected", value: { id: 2 }}, {status: "fulfilled", value: { id: 3 }}, ]
}).catch((err) => {
    console.log(err) // 不会永远执行
})

Promise.any([p1, p2, p3])
.then(res => {
    console.log(res) // {id: 1}
}).catch((err) => {
    // 除非所有都是 reject 才会进入
    console.log(err)
})

Promise.race([p1, p2, p3])
.then(res => {
    console.log(res) // {id: 1}
}).catch((err) => {
    // 如果第一个是 reject 才会进入
    console.log(err)
})
let p1 = new Promise((resolve, reject) => {
    resolve({ id: 1 })
})

let p2 = new Promise((resolve, reject) => {
    reject({ id: 2 })
})

let p3 = new Promise((resolve, reject) => {
    resolve({ id: 3 })
})

 Promise.all([p1, p2, p3])
.then(res => {
    console.log(res)
}).catch((err) => {
    // 有一个 reject 就会进入
    console.log(err) // {id: 2}
})


Promise.allSettled([p1, p2, p3])
.then(res => {
    console.log(res) // [{status: "fulfilled", value: { id: 1 }}, {status: "rejected", value: { id: 2 }}, {status: "fulfilled", value: { id: 3 }}, ]
}).catch((err) => {
    console.log(err) // 不会永远执行
})

Promise.any([p1, p2, p3])
.then(res => {
    console.log(res) // {id: 1}
}).catch((err) => {
    // 除非所有都是 reject 才会进入
    console.log(err)
})

Promise.race([p1, p2, p3])
.then(res => {
    console.log(res) // {id: 1}
}).catch((err) => {
    // 如果第一个是 reject 才会进入
    console.log(err)
})