Promise
优缺点
优点:
- 解决回调嵌套问题(回调地狱)
缺点:
- 无法取消:创建了就会立即执行,无法中途取消
- 如果不设置回调函数,内部抛出的错误外部无法知道
- 当处于
pending状态时,无法知道进度
构造函数
Promise 对象本身就是个构造函数
三种状态
三种状态,确定了就无法改变
pending进行中fulfilled已成功rejected已失败
参数
Promise接受一个function作为参数resolve与reject是接受的function的参数,且这个参数也都是function的
实例方法
then()
Promise 实例添加状态改变时的回调函数
// 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) 的别名,用于指定发生错误时的回调函数。
.catch(err => {
}).catch(err => {
})实现
Promise.prototype.catch() = function(fn) {
return this.then(null, fn);
}Promise.prototype.catch() = function(fn) {
return this.then(null, fn);
}finally()
无参数,无论结果状态如何都会执行,可用于接口调用完成后关闭 loading 状态等
实现:
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()方法,所以此时能保证接口都请求完成了。
const p = Promise.all([p1, p2, p3]);const p = Promise.all([p1, p2, p3]);p 的状态由 p1、p2、p3 决定,分成两种情况:
- 只有
p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 - 只要
p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
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
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 来处理:
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相当于Promise的thentry...catch相当于Promise的catch
测试代码
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)
})
前端知识点