看axios文档的时候发现cancelToken这个东东,这个是用来取消ajax请求的,一般原生的话用的是abort()这个方法。看到这玩意的第一感觉是用起来有点麻烦,但是看了内部实现,发现还是比较有意思的,今天就来分享一下。

    基本使用

    我们先来看看基本用法:

    1. var CancelToken = axios.CancelToken;
    2. var source = CancelToken.source();
    3. axios.get('/user/12345', {//get请求在第二个参数
    4.     cancelToken: source.token
    5. }).catch(function(thrown) {
    6. });
    7. axios.post('/user/12345', {//post请求在第三个参数
    8.     name: 'new name'
    9. }, {
    10.     cancelToken: source.token
    11. });
    12. source.cancel('不想请求了');

    注意,get请求的时候,cancelToken是放在第二个参数里;post的时候,cancelToken是放在第三个参数里。

    我们可以发现,它要先引用axios.CancelToken,然后调用source()方法,会产生一个token和cancel,它的内部到底如何实现,这样做的目的是什么?

    源码分析

    现在我们来看看cancelToken的源码:

    1. 'use strict';
    2. var Cancel = require('./Cancel');
    3. /**
    4. * A `CancelToken` is an object that can be used to request cancellation of an operation.
    5. *
    6. @class
    7. @param {Function} executor The executor function.
    8. */
    9. function CancelToken(executor) {
    10.     if (typeof executor !== 'function') {
    11.         throw new TypeError('executor must be a function.');
    12.     }
    13.     var resolvePromise;
    14.     this.promise = new Promise(function promiseExecutor(resolve) {
    15.         resolvePromise = resolve;
    16.     });
    17.     var token = this;
    18.     executor(function cancel(message) {
    19.         if (token.reason) {
    20.         // Cancellation has already been requested
    21.             return;
    22.         }
    23.         token.reason = new Cancel(message);
    24.         resolvePromise(token.reason);
    25.     });
    26. }
    27. /**
    28. * Throws a `Cancel` if cancellation has been requested.
    29. */
    30. CancelToken.prototype.throwIfRequested = function throwIfRequested() {
    31.     if (this.reason) {
    32.         throw this.reason;
    33.     }
    34. };
    35. /**
    36. * Returns an object that contains a new `CancelToken` and a function that, when called,
    37. * cancels the `CancelToken`.
    38. */
    39. CancelToken.source = function source() {
    40.     var cancel;
    41.     var token = new CancelToken(function executor(c) {
    42.         cancel = c;
    43.     });
    44.     return {
    45.         token: token,
    46.     cancel: cancel
    47.     };
    48. };
    49. module.exports = CancelToken;

    通过源码我们可以发现,CancelToken这个类初始化的时候需要传递一个方法executor,并且它的内部新建了一个promise,最关键的是,它把promise的resolve方法控制权放在了executor方法里面!这种操作代表什么意思?我们看一个小例子:

    1. let resolveHandle;
    2. new Promise((resolve)=>{
    3.     resolveHandle=resolve;
    4. }).then((val)=>{
    5.     console.log('resolve',val);
    6. });
    7. resolveHandle('ok');

    上面的例子中,我们用resolveHandle获取了一个promise的resolve方法的控制权,这样,我们就可以在外部控制这个promise的成功了。要知道new Promise返回的对象是无法从外部决定它成功还是失败的。

    现在来看看source这个方法,我们可以看到,它new了一个CancelToken对象,并传了一个方法executor;采用相同的手法,用cancel变量将executor方法的变量c的控制权拿出来了,那么这个变量c又代表啥呢?

    变量c正是我们前面说到的在CancelToken初始化时,传入executor方法的,也即:

    1. function cancel(message) {
    2.     if (token.reason) {
    3.         // Cancellation has already been requested
    4.         return;
    5.     }
    6.     token.reason = new Cancel(message);
    7.     resolvePromise(token.reason);
    8. }

    也就是说cancel代表的是上面的这个方法,有了这个方法,就可以在外部控制CancelToken内部的promise对象了。

    在source方法中,除了cancel,还有一个token,这个token是CancelToken类的一个实例,可以访问到内部的promise。

    因此CancelToken类如此封装的主要目的就是为了能够分离promise和resolve方法,让用户可以自己调用resolve方法。一旦resolve后,就会触发promise的then方法,现在看看内部promise后的then方法是什么:

    1. if (config.cancelToken) {
    2.     // Handle cancellation
    3.     config.cancelToken.promise.then(function onCanceled(cancel) {
    4.         if (!request) {
    5.             return;
    6.         }
    7.         request.abort();
    8.         reject(cancel);
    9.         // Clean up request
    10.         request = null;
    11.     });
    12. }

    上面是xhr.js的关于cancelToken部分相关代码,可以看到,当用户调用cancel后,就会立即执行abort方法取消请求,同时调用reject让外层的promise失败。

    回到顶部
    我要评论

    所有评论

      相关文章