浅析cancelToken

浅析cancelToken

技术杂谈小彩虹2021-08-16 0:18:36130A+A-

取消请求

1. 为什么要取消请求?

请求的响应时间存在不确定性,请求次数过多时,有可能较早发起的请求会较晚响应。那么我们需要设计一套机制,确保较晚发起的请求可以在客户端就取消掉较早发起的请求。

2. 先自己想想,如何取消一次请求:

  • 发起一次请求A1,且该请求中携带标识此次请求的id
  • 发起一次请求A2(An表示针对同一个接口A的第n次请求),A2请求时取消原请求A1,请求A1被标识为cancelednetwork process工作结束)
  • axios标记A1为请求异常,进入响应处理阶段(响应获取一个错误对象),请求A1结束。

3. 实际上axios是怎么做的?

针对单个请求A发多次的情形(A1,A2...An), An都会带上cancelToken(标记),A(n+1)发起时会将An请求直接canceled(不再处理An请求响应)。

4. 具体实现

axios官网的用例:

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.');

看看axios源码

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

cancelToken.source()是一个工厂函数,返回了一个对象,tokencancel属性都由函数CancelToken构造而成。我们看看CancelToken构造函数做了什么:

// axios/lib/cancel/CancelToken.js
function CancelToken(executor) { // 参数executor

  // 参数类型判断为函数
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }
  
  var resolvePromise;
  // 实例挂载一个promise,这个promise会在变量resolvePromise执行后resolved
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });


  var token = this; // 实例即token
  // 执行器执行,将函数cancel传递到外界
  executor(function cancel(message) {
    if (token.reason) {
      // 如果token挂载了reason属性,说明该token下的请求已被取消
      return;
    }
    // token挂载reason属性
    token.reason = new Cancel(message);
    // 外界可以通过执行resolvePromise来将该token的promise置为resolved
    resolvePromise(token.reason);
  });
}

那么我们将这个tokenpromise resolved有什么用呢?

// axios/lib/adapters/xhr.js
    if (config.cancelToken) {
      // 请求时带上了cancelToken,如果上面token的promise resolve就会执行取消请求的操作
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }

        request.abort();
        reject(cancel);
        // Clean up request
        request = null;
      });
    }

5. 最佳实践

设计一个pendings模块,在每次请求的拦截中进行请求的自动记录取消

// pendings.js
import Axios from 'axios';

const pendings = {};

export default {
  /** * 添加请求 */
  addPending(config) { 
    const { method, url, params, data } = config;
    const id = [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
    const cancel = pendings[id];
    config.cancelToken = config.cancelToken || new Axios.CancelToken(function excutor(cancel) { 
      if (!pendings[id]) { 
        pendings[id] = cancel
      }
    })
    return config;
  },

  /** * 移除请求 */
  removePending(config) { 
    const { method, url, params, data } = config;
    const id = [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
    const cancel = pendings[id];
    if (cancel && typeof cancel === 'function') {
      cancel();
      pendings.delete(identify);
    } 
  },

  /** * 清空所有pending请求 */
  clearPending() { 
    Object.keys(pendings).forEach(c => pending[c]());
  },
}

在你的请求模块中通过axios过滤器来使用:

// request.js
axios.interceptors.request.use((config) => {
    removingPendings(config);
    addPendings(config);
    // ...
})

axios.interceptors.response.use((config) => {
    removingPendings(config);
    addPendings(config);
    // ...
})

6. 总结

cancelToken说白了就是一个promise,这个promise的状态的改变交由使用者自己决定(执行cancel函数)。届时,这个promisethen回调就会执行,取消request

7. 参考

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们