为promise增加abort功能

痞子三分冷 提交于 2020-08-09 02:44:46

Promise只有三种状态:pending、resolve、reject,一个异步的承诺一旦发出,经历等待(pending)后,最终只能为成功或者失败,中途无法取消(abort)。

为promise提供abort功能的思路有两种:

  1. 手动实现abort,触发取消后,异步回来的数据直接丢弃(手动实现,比较稳妥)
  2. 使用原生方法AbortController中断请求(实验中的方法,有兼容性,ie不支持)

手动实现abort方法有两种模式:都是依赖promise的接口间接实现

  • promise race方法

    let PromiseWithAbort = function(promise){
        let _abort = null;
        let Pabort = new Promise((res,rej)=>{
          _abort = function(reason ='abort !'){
            console.warn(reason);
            rej(reason);
          }
        });
    
        let race = Promise.race([promise,Pabort]);
        race.abort = _abort;
        console.log(promise,Pabort);
        return race;
      }
    
    let p1= new Promise(res=>{
       setTimeout(()=>{
          res('p1 success');
       },2000)
    })
    
    let testP = PromiseWithAbort(p1);
    
    testP.then(res=>{
      console.log('success:',res);
    },error=>{
      console.log('error:',error);
    })
    
    testP.abort();
    
    // 结果: reject: abort!
    
  • 重新包装promise

    class PromiseWithAbort {
        constructor(fn){
          let _abort = null;
          let _p = new Promise((res,rej)=>{
            fn.call(null,res,rej);
            _abort = function(error='abort'){ rej(error); }
          })
    
          _p.abort = _abort;
          return _p;
        }
      } 
    
    
    let testP = new PromiseWithAbort((res,rej)=>{
        setTimeout(() => {
          res(1);
        },1000);
      });
    
     testP.then(r=>{
        console.log('res:',r);
      },r=>{
        console.log('rej:',r);
      });
    
      testP.abort();
    //结果: rej: abort
    

AbortController :(这是一个实验中的功能,归属于DOM规范 ,此功能某些浏览器尚在开发中) AbortController接口代表一个控制器对象,允许你在需要时中止一个或多个DOM请求。

// 中断fetch请求
  let controller = new AbortController();
  let signal = controller.signal;

 fetch('https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally',{signal}).then(r=>{
    console.log(r);
  });

  controller.abort();
//结果: Uncaught (in promise) DOMException: The user aborted a request.
//中断一个promise
class PromiseWithAbortController {
  constructor(fn,{signal}){
    if(signal && signal.aborted){
      return Promise.reject(new DOMException('Aborted','AbortError'));
    }

    let _p = new Promise((resolve,reject)=>{
      fn.call(null,resolve,reject);
      if(signal){
        signal.addEventListener('abort',()=>{
          reject(new DOMException('Aborted','AbortError'));
        })
      }
    });

    return _p;
  }
}
let controller = new AbortController();
  let signal = controller.signal;
let testP2 = new PromiseWithAbortController((r,j)=>{
  setTimeout(() => {
    r('success');
  }, 1000);
},{signal});

testP2.then(r=>{
    console.log('res:',r);
  },r=>{
    console.log('rej:',r);
  });

  controller.abort();
  // 结果: rej: DOMException: Aborted

Axios插件自带取消功能:

//1.使用source的token
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');

//2. 通过传出的function
const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // An executor function receives a cancel function as a parameter
    cancel = c;
  })
});

// cancel the request
cancel();

//主要:使用相同token的请求可以一并取消

在现在项目中使用最频繁的是axios,所以取消请求不用担心。dom规范的AbortController,由于兼容性,不推荐使用。如果需要自己动手实现的话,还是文章前两种方法较稳妥(promise race方法和重新包装promise方法)。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!