Axios之取消请求源码分析

文章目录

1.前言

之前看到Axios的取消请求,不像其他的API那么直观好理解,有两种取消请求的方式,感觉很奇怪,所以决定去研究下源码,研究完之后才发现原来如此!

2.源码分析

首先第一种写法的代码:

let Http = this.$http,
   CancelToken = Http.CancelToken,
   that = this;
 Http.post(
     "https://www.fastmock.site/mock/257d9fdebd0b1dd887acd6ec80db8ade/cena/post/test",
     {},
     {
       cancelToken: new CancelToken((e) => {
         that.cancel = e;
       }),
     }
   )
   .then(function (response) {
     console.log(response);
   })
   .catch(function (error) {
     if (Http.isCancel(error)) {
     	//Request canceled 手动取消请求
       console.log("Request canceled", error.message);
     } else {
     ///.....
     
     }
});
this.cancel("手动取消请求");

既然看源码那么就得从入口开始看,CancelTokenaxios的一个属性,首先找到axios.js文件
在这里插入图片描述
再找到CancelToken.js文件,先不要看内部细节,先看整体结构,发现CancelToken就是个构造函数,构造函数上有个source方法,构造函数原型上有throwIfRequestedsubscribeunsubscribe等方法!
在这里插入图片描述
再来看下上面写的这段代码:

{
  cancelToken: new CancelToken((e) => {
    that.cancel = e;
  }),
}

new CancelToken不就是创建实例吗?再来看下CancelToken这个构造函数里面做了啥操作!

function CancelToken(executor) {
	//判断是不是函数
  if (typeof executor !== 'function') {
  //不是函数抛出错误
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;

  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;

  // eslint-disable-next-line func-names
  this.promise.then(function(cancel) {
	//....
  });

  // eslint-disable-next-line func-names
  this.promise.then = function(onfulfilled) {
   //....
  };

  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

从大结构上来看,CancelToken构造函数只有一个promise属性(先不管它是干什么,后面会说),再看executor是什么?不就是new CancelToken的参数,是个function! 执行executor相当下面的代码:

that.cancel = function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
} ;

好了,执行that.cancel方法就是取消请求的关键了!我们再回到上面说的CancelToken构造函数只有一个promise属性!

this.promise = new Promise(function promiseExecutor(resolve) {
   resolvePromise = resolve;
});

看上面代码意思是把resolve赋值给了一个resolvePromise的变量,大家都知道Promise当执行到resolve方法时候,就会执行then里面的回调函数,这里的意思相当把resolve暴露出去,让外面来决定什么时候来执行Promisethen里面的回调函数,这个思路很巧妙!当执行that.cancel的时候,先会去判断有没有创建Cancel实例,有的话就直接退出函数,没有的话就创建Cancel实例,并且触发Promisethen里面的回调函数,先来看下Cancel是什么东西!其实代码非常简单,其实就是设置下message__CANCEL__属性,只要创建了实例,__CANCEL__必须为true!

'use strict';

/**
 * A `Cancel` is an object that is thrown when an operation is canceled.
 *
 * @class
 * @param {string=} message The message.
 */
function Cancel(message) {
  this.message = message;
}

Cancel.prototype.toString = function toString() {
  return 'Cancel' + (this.message ? ': ' + this.message : '');
};

Cancel.prototype.__CANCEL__ = true;

module.exports = Cancel;

接下来是执行Promisethen里面的回调函数了,这里的cancel形参就是token.reasonCancel的实例)!

this.promise.then(function(cancel) {
  if (!token._listeners) return;

  var i;
  var l = token._listeners.length;

  for (i = 0; i < l; i++) {
    token._listeners[i](cancel);
  }
  token._listeners = null;
});

好了接下来,看xhr.js里面的代码,我只列主要一些核相关代码,看下面代码:

 var request = new XMLHttpRequest();


request.onabort = function handleAbort() {
   if (!request) {
     return;
   }

   reject(createError('Request aborted', config, 'ECONNABORTED', request));

   // Clean up request
   request = null;
 };


//省略代码
if (config.cancelToken || config.signal) {
  // Handle cancellation
  // eslint-disable-next-line func-names
  onCanceled = function(cancel) {
    if (!request) {
      return;
    }
    reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
    request.abort();
    request = null;
  };

  config.cancelToken && config.cancelToken.subscribe(onCanceled);
  if (config.signal) {
    config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
  }
}

首先判断配置里面有没有cancelToken 属性,因为这个属性是取消请求属性(一定会有),
接着定义了一个onCanceled函数(没执行),注意request是异步对象,request.abort()执行取消请求并清空异步对象!那么onCanceled函数只要执行了就取消请求了!接下往下看执行了下面这个方法:

config.cancelToken && config.cancelToken.subscribe(onCanceled);

我们再回到CancelToken.jsCancelToken原型上的subscribe方法做了什么?

CancelToken.prototype.subscribe = function subscribe(listener) {
  if (this.reason) {
    listener(this.reason);
    return;
  }

  if (this._listeners) {
    this._listeners.push(listener);
  } else {
    this._listeners = [listener];
  }
};

主要看下面的if判断,如果没有this._listeners,那么this._listeners = [listener];那么是不是this._listeners=[onCanceled],那么再回到上面的Promisethen里面的回调函数,看下面代码!

this.promise.then(function(cancel) {
 if (!token._listeners) return;

 var i;
 var l = token._listeners.length;

 for (i = 0; i < l; i++) {
   token._listeners[i](cancel);
 }
 token._listeners = null;
});

最主要看token._listeners[i] (cancel)这行代码,相当执行下面代码!

(function(cancel) {
    if (!request) {
      return;
    }
    reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
    request.abort();
    request = null;
  })(new Cancel(message))

第一种代码分析就这样了,再看下第二种写法:

let Http = this.$http,
  CancelToken = Http.CancelToken;
  this.source =CancelToken.source();
Http.post(
    "https://www.fastmock.site/mock/257d9fdebd0b1dd887acd6ec80db8ade/cena/post/test",
    {},
    {
      cancelToken: this.source.token
    }
  )
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    if (Http.isCancel(error)) {
      console.log("Request canceled", error.message);
    } else {
    ///
    }
 });
this.source.cancel("手动取消请求");

这里用到了CancelToken构造函数上的source方法!我们来看下源码:

CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

这个方法相当做了一层封装,它返回出来是一个对象,token是CancelToken的实例,cancel是用来取消请求的方法,内部的原理和第一种方法是一样的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值