ES6的promise实现是很可靠的,但是仍然有不足之处,比方说它没有实现取消promise的功能。这导致,只要promise的逻辑开始执行,就没有办法阻止,只能干等着它执行完成。
而很多时候,我们会碰到promise正在处理中,但是我们已经不需要/不关心它的结果的情况。比方说,我们在组件mount完之后发送ajax请求获取页面数据,并交予相关回调处理,但是我们在组件unmount的时候该ajax请求还没有返回结果,此时就需要取消这个promise,让程序不再执行后续的处理逻辑。
ES6没有为我们提供原生的cancel方法,我们需要手动实现。这里我的实现基于对promise的二次封装,以及Promise类的静态方法Promise.race。
/**
* 将原始promise包装成带cancel方法的promise
* @param Promise 原始的promise
* @return Promise 包装后的promise
* */
function withCancel(originalPromise){
let cancel=()=>{}
let isCancelled=false
// 辅助的promise,在调用cancel之后该promise会立即reject
const cancelPromise=new Promise((resolve, reject) => {
cancel=e=>{
isCancelled=true
reject(e)
}
})
// 包装后的promise,本质是Promise.race的返回值,Promise.race传参是原始的promise和辅助的promise
const groupPromise=Promise.race([originalPromise,cancelPromise])
.catch(e=>{
// isCancelled标志位,表明用户是否主动触发cancel。如果是主动触发,不要抛出异常
if(isCancelled){
console.log('promise is cancelled')
console.log(e)
return new Promise(()=>{})
}
else return Promise.reject(e)
})
return Object.assign(groupPromise, {cancel})
}
使用方法如下,在该例子中,原始promise会在3秒后被resolve,并且在then中会打印5。但是我在1.5秒的时候调用了cancel方法,这样原始promise的then中的逻辑就不会执行:
const originalPromise=new Promise((resolve, reject) => {
setTimeout(()=>resolve(5),3000)
})
const promiseWithCancel=withCancel(originalPromise)
promiseWithCancel.then(console.log)
setTimeout(()=>promiseWithCancel.cancel('Hi, this is a cancel message'),1500)
输出如下:
promise is cancelled
Hi, this is a cancel message