Add abort function to promise

Posted by ibechane on Wed, 27 May 2020 17:12:29 +0200

There are only three states of project: pending, resolve, and reject. Once an asynchronous commitment is issued, it can only succeed or fail after it has been pending, and cannot be cancelled in the middle.

There are two ways to provide abort function for promise:

  1. Manually implement abort. After the trigger is cancelled, the data returned asynchronously will be discarded directly (manually, relatively stable)
  2. Interrupt request using native method AbortController (experimental method, compatible, not supported by ie)

There are two modes to implement abort method manually: both of them depend on the indirect implementation of promise interface

  • promise race method

    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();
    
    // Result: reject: abort!
    
    
    
    
  • Repack 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);
        }, 2000);
      });
    
     testP.then(r=>{
        console.log('res:',r);
      },r=>{
        console.log('rej:',r);
      });
      
      testP.abort();
    //Results: rej: abort
    
    
    
    

**AbortController: (* * this is an experimental function, which belongs to the DOM specification. Some browsers of this function are still under development.) * the AbortController * * interface represents a controller object, allowing you to abort one or more DOM requests when necessary.
Interrupt fetch request:

  let controller = new AbortController();
  let signal = controller.signal;

  let p3 = new Promise((res,rej)=>{
    setTimeout(() => {
      res('success');
    }, 2000);
  },{signal});

  p3.then(r=>{console.log(r);
  },r=>{console.log(r);
  });

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

  controller.abort();
//Results: Uncaught (in promise) DOMException: The user aborted a request

Interrupt a 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 testP2 = new PromiseWithAbortController((r,j)=>{
  setTimeout(() => {
    r('success');
  }, 1000);
},{signal});

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

  controller.abort();
  // Result: rej: DOMException: Aborted

The Axios plug-in has its own cancellation function:

//1. Use the token of source
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. Through the outgoing 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();

//Main: requests using the same token can be cancelled at the same time

Topics: Front-end axios IE Javascript