vaakian / vaakian.github.io

some notes
https://vaakian.github.io
3 stars 0 forks source link

Axios/Fetch/XMLHttpRequest #24

Open vaakian opened 2 years ago

vaakian commented 2 years ago

Axios

特点:拦截器,前后端通用。

const axios = require('axios')
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
/*
取消请求方法1:
source.cancel 是取消函数
source.token 赋给需要被控制的请求
如果token放在多个请求上,用source可以1次cancel多个请求
*/

// 创建实例
const instance = axios.create({
    baseURL: 'https://api.github.com/users/',
    timeout: 1000,
    headers: { 'user-agent': 'Chrome' }
});

instance.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    // 注入canceltoken,调用source就会取消config有cancelToken的所有请求。
    config.cancelToken = source.token;
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    console.log('收到响应');
    return response;
}, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
});
instance.get('/vaakian')
    .then(data => console.log(data))
    .catch(reason => {
        // 判断是不是被cancel了
        if (axios.isCancel(reason)) {
            console.log('请求被取消');
        }
    })

source.cancel();

取消请求方法有两种,一种是上面的工厂模式创建单例,多个请求可以复用一个cancelTtoken,可以一次canel多个请求。 而下面这种,每个请求单独设定一个cancelToken,一次canel一个请求,多用于重复请求时,取消上一个请求。

let cancelSources = [];

instance.interceptors.request.use(function (config) {
    // 先取消上一个请求
    for (let i in cancelSources) {
        let source = cancelSources[i];
        if (
            // 方法和url相同,则认为是相同的请求
            source.config.method === config.method &&
            source.config.url === config.url
        ) {
            // 取消并移除
            source.cancel();
            cancelSources.splice(i, 1);
            break;
        }
    }
    // 请求前先挂载canceltoken
    config.cancelToken = new cancelToken((cancel) => {
        // 回调函数将cancel函数存起来,对应一个config(请求)
        cancelSources.push({ config, cancel });
    })
    return config;
}, function (error) {
    return Promise.reject(error);
});

如何捕获cancel的请求? 当一个请求被取消后,promise状态会变为reject,然后在catch中使用axios提供的isCancel来判断是否是被取消了。

instance.get('/vaakian')
    .then(data => {
        console.log(data);
    })
    .catch(reason => {
        // 判断是不是被cancel了
        if (axios.isCancel(reason)) {
            console.log('请求被取消');
        }
    })
vaakian commented 2 years ago

fetch

fetch是现在浏览器都内置的API,使用Promise,不使用回调函数,与axios的请求语法非常相似。 fetch没有自带拦截器,开发者可以自行实现。 fetch不会将字符串json数据自动转换,依然是字符串,但可以调用data.json()取得,该方法返回一个promise

fetch(url, config)
  .then(data => console.log(data)) // 不会自动将json字符串转换为对象

// 手动转json
fetch(url, config)
  .then(data => data.json())
  .then(jsonData => console.log(jsonData)) // 此时为json对象

fetch取消请求,或超时抛弃

取消请求可以用AbortController对象来处理,超时抛弃可以用Promise.race,但Promise.race方法请求会继续直到自然终止,只是代码逻辑上将该请求抛弃,不再处理。

const controller = new AbortController();
fetch('https://api.github.com/users/vaakian', {
    signal: controller.signal
}).then(data => console.log(data))
    .catch(reason => console.log(reason));
// 200ms超时取消
setTimeout(controller.abort, 200);

Promise.race方法实现请求超时,但浏览器请求不会被终止。

const countTimeout = function (duration) {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject('request timeout'), duration)
    });
}

let request = fetch('https://api.github.com/users/vaakian');
// 200ms后超时
Promise.race([request, countTimeout(200)])
    .then(data => console.log(data))
    .catch(reason => console.log(reason))
vaakian commented 2 years ago

XMLHttpRequest

老大哥了,最先出现的异步加载方案。 jQuery中的$.ajax方法,就是基于XHR来封装实现的。 与axiosfetch不同的是,$.ajax和原生XHR都通过回调函数来获取返回值。 原因也很好猜,PromiseXHR时代还没有出现过,回调函数是普遍用法。

function sendAjax() {
  //构造表单数据
  let formData = new FormData();
// url中params的参数
 // formData.append('user', 'vaakian');
 // formData.append('id', '786');
  //创建xhr对象 
  let xhr = new XMLHttpRequest();
  //设置xhr请求的超时时间
  xhr.timeout = 3000;
  //设置响应返回的数据格式
  xhr.responseType = "json";
  //创建一个 get 请求,采用异步
  xhr.open('get', '/users/vaakian', true);
  //注册相关事件回调处理函数
  xhr.onload = function(e) { 
    iconsole.log(this.responseText);
  };
  xhr.ontimeout = function(e) { ... };
  xhr.onerror = function(e) { ... };
  xhr.upload.onprogress = function(e) { ... };

  //发送数据
  xhr.send(formData);
}