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
的then
try...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)
})