轻松掌握axios核心源码

442 阅读4分钟

axios 是一个基于 PromiseHTTP 请求库,它的内部实现通过几个核心步骤来发送请求。下面详细解释了这些步骤。

1. axios(url, options) 发生了什么

发送请求流程:

function request(config){
    let promise = Promise.resolve(config);
    let chains = [dispatchRequest, undefined];// undefined 占位
    //调用 then 方法指定回调
    let result = promise.then(chains[0], chains[1]);
    //返回 promise 的结果
    return result;
}

function dispatchRequest(config){
    // 发送请求前数据格式的转换 ......

    xhrAdapter(config).then(response => {
        //响应的结果进行转换处理 ......

        return response;
    }, error => {
        // 发送请求前数据格式的转换 ......

        throw error;
    });
}
function xhrAdapter(config) {
    return new Promise((resolve, reject) => {
        //定义 AJAX 请求
        let xhr = new XMLHttpRequest();
        xhr.open(config.method, config.url);
        // 把config上的一些参数、事件监听等,配置到xhr对象上.......
​
        //发送请求
        xhr.send();
        //监听事件
        xhr.onreadystatechange = function(){
            if (xhr.readyState !== 4) {
                // 如果请求还未完成,则退出
                return;
            }
            if(xhr.readyState === 4){
                //判断成功的条件
                // xhr.status >= 200 && xhr.status < 300 可以通过自定义传参控制
                if(xhr.status >= 200 && xhr.status < 300){
                    //成功的状态
                    resolve({
                        //配置对象
                        config: config,
                        //响应体
                        data: xhr.response,
                        //响应头
                        headers: xhr.getAllResponseHeaders(), //字符串  parseHeaders
                        // xhr 请求对象
                        request: xhr,
                        //响应状态码
                        status: xhr.status,
                        //响应状态字符串
                        statusText: xhr.statusText
                    });
                }else{
                    //失败的状态
                    reject(new Error({
                        message: 'Request failed with status code ' + xhr.status,
                        // 错误信息属性......
                    }));
                }
            }
        }
    });
}
  1. 返回Promiseaxios 的请求返回的是一个Promise
  2. xhrAdapter:默认的适配器,负责实际的 XMLHttpRequest 请求。它处理请求的发送、响应的处理和错误的捕获。

2. 请求和响应拦截器


axios 允许通过拦截器(interceptors)来修改请求和响应。我们可以在请求被发送之前对请求进行处理,或在响应到达后对其进行修改。

当我们使用 interceptors.request.use(fulfilled, rejected):

function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected,
  });
}

在调用 use 方法时,我们把 fulfilled 和 rejected 作为回调添加到拦截器的 handlers 数组中。之后需要用到时会遍历执行。

function(config){
    let promise = Promise.resolve(config);
    //创建数组,里面存放请求拦截器,XMLHttpRequest 请求和响应拦截器
    const chains = [dispatchRequest, undefined];
    //处理拦截器
    // 遍历请求拦截器的handlers,将回调函数添加到chains前面
    this.interceptors.request.handlers.forEach(item => {
        chains.unshift(item.fulfilled, item.rejected);
    });
    // 遍历响应拦截器的handlers,将回调函数添加到 chains 的后面
    this.interceptors.response.handlers.forEach(item => {
        chains.push(item.fulfilled, item.rejected);
    });

    //遍历chains中每一个函数(请求拦截器,XMLHttpRequest 请求和响应拦截器)
    while(chains.length > 0){
        promise = promise.then(chains.shift(), chains.shift());
    }
    return promise;
}

每次执行都是从chains数组中去取出两个函数(成功和失败的回调)执行,所以需要[dispatchRequest, undefined]保证chains的个数一定是偶数

3. 取消请求


有时我们希望在某些情况下取消发起的 HTTP 请求(例如,用户快速切换页面或模块功能时)。axios 提供了 CancelToken 来实现这一功能。
取消请求的基本用法:

const { cancel, token } = axios.CancelToken.source();
config.cancelToken = token; // 将 cancelToken 添加到请求配置中
axios(config); // 发起请求

// 在需要取消请求时调用 cancel() 方法
cancel();

cancel 保存起来,需要取消时调用 cancel() 就可以取消对应的请求

CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  // 此时已经 cancel=c
  return {
    token: token,
    cancel: cancel,
  };
};

// 根据下方代码可知:cancle=c=function() {resolvePromise()}
  
function CancelToken(executor) {
  //声明一个变量,保存到闭包中
  var resolvePromise;
  // 保存一个promise,后面用来执行回调函数取消请求
  this.promise = new Promise((resolve) => {
    // 将 resolve 赋值给 resolvePromise
    resolvePromise = resolve;
  });
  //调用 executor 函数
  executor(function() {
    // resolvePromise 保存在闭包中
    resolvePromise();
  });
}

为什么 cancel() 可以取消请求?
因为在发送 ajax 请求前,会监听 token.promise ,当我们执行 cancel()时会改变 promise 的状态,对应的回调函数就会执行,然后取消 ajax 请求

function xhrAdapter() {
  //定义 AJAX 请求
  let xhr = new XMLHttpRequest();
  xhr.open(config.method, config.url);
  // 其他请求配置...

  if (config.cancelToken) {
    // Handle cancellation
    config.cancelToken.promise.then(function onCanceled(cancel) {
      if (!xhr) {
        return;
      }
      hr.abort(); // 取消请求
      reject(cancel); // 触发错误回调
      xhr = null; // 清理请求对象
    });
  }
}

总结


通过了解 axios 的核心原理实现,我们能够更好地掌握请求的处理过程。axios 利用 Promise 链、拦截器机制和取消请求功能,提供了优秀的扩展性。在实际开发中,了解这些实现细节可以帮助我们更好地优化请求逻辑,提升开发效率。

希望这篇文章能帮助你更深入地理解 axios 的工作原理以及如何更高效地利用它进行 HTTP 请求操作✊️✊️