Promise 有个缺点就是一旦建立就没法取消,因此本质上 Promise 是没法被终止的,但咱们在开发过程当中可能会遇到下面两个需求:promise
就是在某个 then/catch 执行以后,不想让后续的链式调用继续执行了,即:网络
somePromise
.then(() => {})
.then(() => {
// 终止 Promise 链,让下面的 then、catch 和 finally 都不执行
})
.then(() => console.log('then'))
.catch(() => console.log('catch'))
.finally(() => console.log('finally'))
复制代码
答案就是在 then/catch 的最后一行返回一个永远 pending 的 promise 便可:app
return new Promise((resolve, reject) => {})
复制代码
这样的话后面全部的 then、catch 和 finally 都不会执行了。dom
注意这里是中断而不是终止,由于 Promise 没法终止,这个中断的意思是:在合适的时候,把 pending 状态的 promise 给 reject 掉。例如一个常见的应用场景就是但愿给网络请求设置超时时间,一旦超时就就中断,咱们这里用定时器模拟一个网络请求,随机 3 秒以内返回:函数
const request = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('收到服务端数据')
}, Math.random() * 3000)
})
复制代码
若是认为超过 2 秒就是网络超时,能够对该 promise 写一个包装函数 timeoutWrapper:ui
function timeoutWrapper(p, timeout = 2000) {
const wait = new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求超时')
}, timeout)
})
return Promise.race([p, wait])
}
复制代码
因而就能够像下面这样用了:spa
const req = timeoutWrapper(request)
req.then(res => console.log(res)).catch(e => console.log(e))
复制代码
不过这种方式并不灵活,由于终止 promise 的缘由可能有不少,例如当用户点击某个按钮或者出现其余事件时手动终止。因此应该写一个包装函数,提供 abort 方法,让使用者本身决定什么时候终止:code
function abortWrapper(p1) {
let abort
let p2 = new Promise((resolve, reject) => (abort = reject))
let p = Promise.race([p1, p2])
p.abort = abort
return p
}
复制代码
使用方法以下:事件
const req = abortWrapper(request)
req.then(res => console.log(res)).catch(e => console.log(e))
setTimeout(() => req.abort('用户手动终止请求'), 2000) // 这里能够是用户主动点击
复制代码
最后,再次强调一下,虽然 promise 被中断了,可是 promise 并无终止,网络请求依然可能返回,只不过那时咱们已经不关心请求结果了。开发