一个正在执行中的promise怎样被取消?
其实就像一个执行中的ajax要被取消一样,ajax有abort()进行取消,而且fetch api 也有了相关的规范-【AbortController】。
fetch 怎样取消?
先来看下如何取消一个fetch请求
const url = "https://bigerfe.com/api/xxxx" let controller; let signal; function requestA(){ if (controller !== undefined) { controller.abort(); //终止请求 } if ("AbortController" in window) { controller = new AbortController; signal = controller.signal; } fetch(url, {signal}) .then((response) => { //do xxx updateAutocomplete() }) .catch((error) => { //do xxx handleError(error); }) }); }
怎样实现实现promise的取消?
方案1 - 借助reject 方法
我们都知道一个promise对象状态的改变是通过resolve和reject来执行的。那是不是可以借助reject方法来模拟呢?
上代码
//返回一个promise和abort方法 function getPromise() { let _res, _rej; const promise = new Promise((resolve, reject) => { _res = resolve; _rej = reject; setTimeout(() => { resolve('123') }, 5000); }); return { promise, abort: () => { _rej({ name: "abort", message: "the promise is aborted", aborted: true, }); } }; } const { promise, abort } = getPromise(); promise.then(console.log).catch(e => { console.log(e); }); abort();
上面的方法可以正常执行,但是不够通用,可以将Promise构造函数内的逻辑提取出来,作为一个回调传进去。
改造一下
function getPromise(cb) { let _res, _rej; const promise = new Promise((res, rej) => { _res = res; _rej = rej; cb && cb(res,rej); }); return { promise, abort: () => { _rej({ name: "abort", message: "the promise is aborted", aborted: true, }); } }; } //主逻辑提取出来 function runCb(resolve,reject){ setTimeout(()=>{ resolve('1111') },3000) } const { promise, abort } = getPromise(runCb); promise.then(console.log).catch(e => { console.log(e); });
方案2 - 借助 Promise.race() 方法
相信大家都知道race方法的作用,这里还是简单介绍下。
当有若干个promise, p1, p2, p3…在调用, let p = Promise.race([p1, p2, p3,…])的时候,返回的p也是一个promise。那么p什么时候会被resolve或者被reject呢?
看race我们知道它是竞速或赛跑的意思,所以p1, p2, p3 … 最先一个被resolve或者被reject的结果就是p的resolve或者reject的结果。所以后续的promise的resolve和reject都不会再被执行了。
代码很简单,其实够短小精悍。
//传入一个正在执行的promise function getPromiseWithAbort(p){ let obj = {}; //内部定一个新的promise,用来终止执行 let p1 = new Promise(function(resolve, reject){ obj.abort = reject; }); obj.promise = Promise.race([p, p1]); return obj; }
调用
var promise = new Promise((resolve)=>{ setTimeout(()=>{ resolve('123') },3000) }) var obj = getPromiseWithAbort(promise) obj.promise.then(res=>{console.log(res)}) //如果要取消 obj.abort('取消执行')
借助race方法明显的更简洁,更易用。
最后
其实取消promise执行和取消请求是一样的,并不是真的终止了代码的执行,而是对结果不再处理。另外fetch api虽然增加了新的标准实现,但仍然存在兼容问题,而且只能在浏览器中使用。那么非浏览器的环境中呢?比如RN?所以如果想要达到一种通用的方式,那么本文的取消promise的方式应该是个不错的方式。
目前知名的axios库也有abort能力,回头看下它的实现方式,也欢迎小伙伴们留言讨论。
---end,希望对你有用。