axios 之cancelToken原理以及使用


看axios文檔的時候發現cancelToken這個東東,這個是用來取消ajax請求的,一般原生的話用的是abort()這個方法。看到這玩意的第一感覺是用起來有點麻煩,但是看了內部實現,發現還是比較有意思的,今天就來分享一下。

基本使用

我們先來看看基本用法:


    var CancelToken = axios.CancelToken;
    var source = CancelToken.source();
    axios.get('/user/12345', {//get請求在第二個參數
     cancelToken: source.token
    }).catch(function(thrown) {
    });
    axios.post('/user/12345', {//post請求在第三個參數
     name: 'new name'
    }, {
     cancelToken: source.token
    });
    source.cancel('不想請求了');

注意,get請求的時候,cancelToken是放在第二個參數里;post的時候,cancelToken是放在第三個參數里。

我們可以發現,它要先引用axios.CancelToken,然后調用source()方法,會產生一個token和cancel,它的內部到底如何實現,這樣做的目的是什么?

源碼分析

現在我們來看看cancelToken的源碼:


    'use strict';
    var Cancel = require('./Cancel');
    /**
     * A `CancelToken` is an object that can be used to request cancellation of an operation.
     *
     * @class
     * @param {Function} executor The executor function.
     */
    function CancelToken(executor) {
     if (typeof executor !== 'function') {
     throw new TypeError('executor must be a function.');
     }
     var resolvePromise;
     this.promise = new Promise(function promiseExecutor(resolve) {
     resolvePromise = resolve;
     });
     var token = this;
     executor(function cancel(message) {
     if (token.reason) {
     // Cancellation has already been requested
     return;
     }
     token.reason = new Cancel(message);
     resolvePromise(token.reason);
     });
    }
    /**
     * Throws a `Cancel` if cancellation has been requested.
     */
    CancelToken.prototype.throwIfRequested = function throwIfRequested() {
    	if (this.reason) {
     throw this.reason;
     }
    };
    /**
     * Returns an object that contains a new `CancelToken` and a function that, when called,
     * cancels the `CancelToken`.
     */
    CancelToken.source = function source() {
     var cancel;
     var token = new CancelToken(function executor(c) {
     cancel = c;
     });
     return {
     token: token,
     cancel: cancel
     };
    };
    module.exports = CancelToken;

通過源碼我們可以發現,CancelToken這個類初始化的時候需要傳遞一個方法executor,並且它的內部新建了一個promise,最關鍵的是,它把promise的resolve方法控制權放在了executor方法里面!這種操作代表什么意思?我們看一個小例子:


    let resolveHandle;
    new Promise((resolve)=>{
    	resolveHandle=resolve;
    }).then((val)=>{
     console.log('resolve',val);
    });
    resolveHandle('ok');

上面的例子中,我們用resolveHandle獲取了一個promise的resolve方法的控制權,這樣,我們就可以在外部控制這個promise的成功了。要知道new Promise返回的對象是無法從外部決定它成功還是失敗的。

現在來看看source這個方法,我們可以看到,它new了一個CancelToken對象,並傳了一個方法executor;采用相同的手法,用cancel變量將executor方法的變量c的控制權拿出來了,那么這個變量c又代表啥呢?

變量c正是我們前面說到的在CancelToken初始化時,傳入executor方法的,也即:

    

    function cancel(message) {
    	if (token.reason) {
    	// Cancellation has already been requested
    	return;
     }
    	token.reason = new Cancel(message);
     resolvePromise(token.reason);
    }

也就是說cancel代表的是上面的這個方法,有了這個方法,就可以在外部控制CancelToken內部的promise對象了。

在source方法中,除了cancel,還有一個token,這個token是CancelToken類的一個實例,可以訪問到內部的promise。

因此CancelToken類如此封裝的主要目的就是為了能夠分離promise和resolve方法,讓用戶可以自己調用resolve方法。一旦resolve后,就會觸發promise的then方法,現在看看內部promise后的then方法是什么:

    if (config.cancelToken) {
     // Handle cancellation
     config.cancelToken.promise.then(function onCanceled(cancel) {
     if (!request) {
     return;
     }
     request.abort();
     reject(cancel);
     // Clean up request
     request = null;
     });
     }

上面是xhr.js的關於cancelToken部分相關代碼,可以看到,當用戶調用cancel后,就會立即執行abort方法取消請求,同時調用reject讓外層的promise失敗。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM