一线大厂高级前端编写,前端初中阶面试题,帮助初学者应聘,需要联系微信:javadudu

关于使用axios发起网络请求的异步问题

我们都知道,发起网络请求这个任务,是一个异步任务。

大家可以看看以下代码中,网络请求中携带的参数是什么:

const total = 500;
const params = {
    optionsA: "a",
    optionsB: "b",
    optionsC: "c",
    page:{
        currentPage: 1,
        pageSize: 20,
    }
};
const maxPage = Math.ceil(total / params.page.currentPage)
while(params.page.currentPage <= maxPage){
    // 发起网络请求
    axios.post('https://xxxxx.xxx.com/xxxx', params);
    // 页码增加
    params.page.currentPage++;
}

上述代码的本意是为了获取当前参数下所有的数据,所以会从第一页开始获取数据。但得到的输出结果却是下面的这个

// 这里模仿的是在浏览器按下F12进入控制台中网络请求中的请求体
// 第一次请求
data:{
    optionsA: "a",
    optionsB: "b",
    optionsC: "c",
    page:{
        currentPage: 26,
        pageSize: 20,
    }
}

// 第二次请求
data:{
    optionsA: "a",
    optionsB: "b",
    optionsC: "c",
    page:{
        currentPage: 26,
        pageSize: 20,
    }
}

// 第三次请求
data:{
    optionsA: "a",
    optionsB: "b",
    optionsC: "c",
    page:{
        currentPage: 26,
        pageSize: 20,
    }
}

......

// 一共25次请求中的currentPage都是一样的

因为超出边界的缘故,所以每一次返回回来的数据都是null。

于是这里我就开始好奇了,为什么每次请求发出的currentPage都是26呢,为什么不是从1递增到25(对应上述我定义的maxSize)

我当然知道网络请求是异步的,但在过往我的认知中,这个异步是指其请求过程以及其请求结果的异步,也就是我们发出请求是同步的,而在请求回来以及后续处理的过程是属于异步的。

如果按这个思路走,那么就应该是从第1页递增到第25页才对啊。

我猜测一共有两种可能

  1. axios将请求的发送放到了异步当中
  2. xhrHttpRequest本身就是异步发送请求的

为了验证我的猜想,我就去翻了翻axios的源码

相关路径在node_modules/axios/lib

首先,我们顺着axios.[methods]的方向往下找,很快就找到了相关的源码,源码部分有兴趣的可以看看,懒得看得看我的文字也够了

// node_modules/axios/lib/core/Axios.js

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

utils是一个工具库,有兴趣的可以去看看它的中文文档,utils中文文档|utils js中文教程|解析 | npm中文文档 (npmdoc.org)

上述代码的forEach和Array.prototype.map有点相似,return的值会成为新数组中的对应元素,当然这里没用到,这里是起到了遍历的操作。

顺着往下找,我们找到了this.request的定义

// node_modules/axios/lib/core/Axios.js

/**
 * Dispatch a request
 *
 * @param {Object} config The config specific for this request (merged with this.defaults)
 */
Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = utils.merge({
      url: arguments[0]
    }, arguments[1]);
  }

  config = utils.merge(defaults, {method: 'get'}, this.defaults, config);
  config.method = config.method.toLowerCase();

  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

我们重点看21行及其之后的代码片段

可以看到,在21行定义了一个chain变量,这是作为一个类似于钩子函数队列的存在,也就是存放我们的请求拦截器和响应拦截器的地方,24行就是往队列首部添加请求拦截器,28行就是往队列尾部添加响应拦截器

其实从这里,就已经可以看出本篇文章所需要寻求的答案

最后我们在33行可以看到,从chain队列中取任务的操作通过promise的then方法放入到了微任务队列中,并且通过then链式调用,最后调用到我们的dispatchRequest方法(发送请求的方法,由原生xhrHttpRequest或者http模块封装)

所以通过以上研究可以明白,axios中的异步,这是真的从开始到结束,都是在异步完成的,再回到一开始的问题,如果想要并发多个请求,可以更改代码为以下内容

const total = 500;
const page = 1;
const pageSize = 20;
const maxPage = Math.ceil(total / pageSize)
while(page <= maxPage){
    // 定义参数
    const params = {
        optionsA: "a",
        optionsB: "b",
        optionsC: "c",
        page:{
            currentPage: page,
            pageSize: pageSize,
        }
    };
    // 发起网络请求
    axios.post('https://xxxxx.xxx.com/xxxx', params);
    // 页码增加
    page++;
}

好了我要加班去了,拜拜咯,喜欢的话给我点个赞,收藏,评论一下都好,让我开心开心谢谢你们!!

如果有不对的,欢迎大佬们多指导一下!!!

原文链接:https://juejin.cn/post/7311728260135731219 作者:笑心

(0)
上一篇 2023年12月14日 上午10:27
下一篇 2023年12月14日 上午10:37

相关推荐

发表评论

登录后才能评论