這篇文章給大家分享的是有關JavaScript如何中斷請求的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
Promise有一個缺點是一旦創建無法取消,所以本質上Promise是無法被終止的.
但是我們可以通過中斷調用鏈或中斷Promise來模擬請求的中斷.
中斷調用鏈就是在某一個then/catch執行之后,后續的鏈式調用(包括then,catch,finally)不再繼續執行.
方法是在then/catch返回一個新的Promise實例,并保持pending狀態:
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result');
});
}).then(res => {
// 達到某種條件,return一個pending狀態的Promise實例,以中斷調用鏈
if (res === 'result') {
return new Promise(() => {});
}
console.log(res); // 不打印
}).then(() => {
console.log('then不執行'); // 不打印
}).catch(() => {
console.log('catch不執行'); // 不打印
}).finally(() => {
console.log('finally不執行'); // 不打印
});中斷Promise不等同于中止Promise,因為Promise是無法被終止的.
這里的中斷指的是,在合適的時機,把pending狀態的promise給reject掉.例如一個常見的應用場景就是給網絡請求設置超時時間,一旦超時就中斷.
老規矩,用setTimeout來模擬網絡請求.閥值設置為Math.random() * 3000表示隨機3秒之內返回結果.
const request = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('收到服務端數據')
}, Math.random() * 3000)
})假設超過2秒就是網絡超時,我們可以封裝一個超時處理函數.
由于網絡請求所需的事件是隨機的,因此可以利用Promise.race方法,達到超時reject的目的.
const timeoutReject = (p1, timeout = 2000) => {
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('網絡超時');
}, timeout);
});
return Promise.race([p1, p2]);
};
timeoutReject(request).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});上面實現的方式并不靈活,因為中斷Promise的方式有很多,不單單是網絡超時.
我們可以仿照Axios中CancelToken的核心源碼,簡單包裝一個abort方法,供使用者隨時調用.
function abortWrapper(p1) {
let abort;
const p2 = new Promise((resolve, reject) => {
abort = reject;
});
// 如果沒有resolve或reject,p2的狀態永遠是pending
const p = Promise.race([p1, p2]);
p.abort = abort;
return p;
}
const req = abortWrapper(request);
req.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
setTimeout(() => {
// 手動調用req.abort,將p2的狀態改變為rejected
req.abort('手動中斷請求');
}, 2000);如此封裝的主要目的就是為了能夠在Promise外部控制其resolve或reject,讓使用者可以隨時手動調用resolve(觸發.then)或reject(觸發.catch).
需要注意的是,雖然Promise請求被中斷了,但是promise并沒有終止,網絡請求依然可能返回,只不過那時我們已經不關心請求結果了.
rxjs本身提供了取消訂閱的方法,即unsubscribe.
let stream1$ = new Observable(observer => {
let timeout = setTimeout(() => {
observer.next('observable timeout');
}, 2000);
return () => {
clearTimeout(timeout);
}
});
let disposable = stream1$.subscribe(value => console.log(value));
setTimeout(() => {
disposable.unsubscribe();
}, 1000);Axios的CancelToken有兩種使用方法:
方法一
import axios from '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
}
});
source.cancel('Operation canceled by the user.');方法二
import axios from 'axios';
const CancelToken = axios.CancelToken;
// 創建一個變量如 cancel 用于存儲這個中斷某個請求的方法
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c; // 將參數 c 賦值給 cancel
})
});
// 判斷 cancel 是否為函數,確保 axios 已實例化一個CancelToken
if (typeof cancel === 'function') {
cancel();
cancel = null;
}CancelToken的核心源碼:(axios/lib/cancel/CancelToken.js)
'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;可以看到,在Axios底層,CancelToken的核心源碼所體現的思想,與上面中斷Promise包裝abort方法的思想一致.
只不過Axios在外部手動調用resolve(用戶觸發cancel方法),而resolve一旦調用,就會觸發promise的then方法,來看這個promise.then的源碼:(axios/lib/adapters/xhr.js)
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}可以看到then方法中會執行abort方法取消請求,同時調用reject讓外層的promise失敗.
感謝各位的閱讀!關于“JavaScript如何中斷請求”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。