JavaScript 中斷請(qǐng)求幾種方案詳解
1 Promise
Promise有一個(gè)缺點(diǎn)是一旦創(chuàng)建無法取消,所以本質(zhì)上Promise是無法被終止的.
但是我們可以通過中斷調(diào)用鏈或中斷Promise來模擬請(qǐng)求的中斷.
中斷調(diào)用鏈
中斷調(diào)用鏈就是在某一個(gè)then/catch執(zhí)行之后,后續(xù)的鏈?zhǔn)秸{(diào)用(包括then,catch,finally)不再繼續(xù)執(zhí)行.
方法是在then/catch返回一個(gè)新的Promise實(shí)例,并保持pending狀態(tài):
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result');
});
}).then(res => {
// 達(dá)到某種條件,return一個(gè)pending狀態(tài)的Promise實(shí)例,以中斷調(diào)用鏈
if (res === 'result') {
return new Promise(() => {});
}
console.log(res); // 不打印
}).then(() => {
console.log('then不執(zhí)行'); // 不打印
}).catch(() => {
console.log('catch不執(zhí)行'); // 不打印
}).finally(() => {
console.log('finally不執(zhí)行'); // 不打印
});
中斷Promise
中斷Promise不等同于中止Promise,因?yàn)镻romise是無法被終止的.
這里的中斷指的是,在合適的時(shí)機(jī),把pending狀態(tài)的promise給reject掉.例如一個(gè)常見的應(yīng)用場景就是給網(wǎng)絡(luò)請(qǐng)求設(shè)置超時(shí)時(shí)間,一旦超時(shí)就中斷.
老規(guī)矩,用setTimeout來模擬網(wǎng)絡(luò)請(qǐng)求.閥值設(shè)置為Math.random() * 3000表示隨機(jī)3秒之內(nèi)返回結(jié)果.
const request = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('收到服務(wù)端數(shù)據(jù)')
}, Math.random() * 3000)
})
假設(shè)超過2秒就是網(wǎng)絡(luò)超時(shí),我們可以封裝一個(gè)超時(shí)處理函數(shù).
由于網(wǎng)絡(luò)請(qǐng)求所需的事件是隨機(jī)的,因此可以利用Promise.race方法,達(dá)到超時(shí)reject的目的.
const timeoutReject = (p1, timeout = 2000) => {
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('網(wǎng)絡(luò)超時(shí)');
}, timeout);
});
return Promise.race([p1, p2]);
};
timeoutReject(request).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
包裝abort方法——仿照Axios的CancelToken
上面實(shí)現(xiàn)的方式并不靈活,因?yàn)橹袛郟romise的方式有很多,不單單是網(wǎng)絡(luò)超時(shí).
我們可以仿照Axios中CancelToken的核心源碼,簡單包裝一個(gè)abort方法,供使用者隨時(shí)調(diào)用.
function abortWrapper(p1) {
let abort;
const p2 = new Promise((resolve, reject) => {
abort = reject;
});
// 如果沒有resolve或reject,p2的狀態(tài)永遠(yuǎn)是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(() => {
// 手動(dòng)調(diào)用req.abort,將p2的狀態(tài)改變?yōu)閞ejected
req.abort('手動(dòng)中斷請(qǐng)求');
}, 2000);
如此封裝的主要目的就是為了能夠在Promise外部控制其resolve或reject,讓使用者可以隨時(shí)手動(dòng)調(diào)用resolve(觸發(fā).then)或reject(觸發(fā).catch).
需要注意的是,雖然Promise請(qǐng)求被中斷了,但是promise并沒有終止,網(wǎng)絡(luò)請(qǐng)求依然可能返回,只不過那時(shí)我們已經(jīng)不關(guān)心請(qǐng)求結(jié)果了.
2 RXJS的unsubscribe方法
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);
3 Axios的CancelToken
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;
// 創(chuàng)建一個(gè)變量如 cancel 用于存儲(chǔ)這個(gè)中斷某個(gè)請(qǐng)求的方法
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c; // 將參數(shù) c 賦值給 cancel
})
});
// 判斷 cancel 是否為函數(shù),確保 axios 已實(shí)例化一個(gè)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的核心源碼所體現(xiàn)的思想,與上面中斷Promise包裝abort方法的思想一致.
只不過Axios在外部手動(dòng)調(diào)用resolve(用戶觸發(fā)cancel方法),而resolve一旦調(diào)用,就會(huì)觸發(fā)promise的then方法,來看這個(gè)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方法中會(huì)執(zhí)行abort方法取消請(qǐng)求,同時(shí)調(diào)用reject讓外層的promise失敗.
到此這篇關(guān)于JavaScript 中斷請(qǐng)求幾種方案詳解的文章就介紹到這了,更多相關(guān)js中斷請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 寫入cookie的JavaScript代碼庫 cookieLibrary.js
- 從基礎(chǔ)開始建立一個(gè)JS代碼庫
- java FastJson的簡單用法
- Vue.js性能優(yōu)化N個(gè)技巧(值得收藏)
- angularJS實(shí)現(xiàn)表格部分列展開縮起示例代碼
- JavaScript實(shí)現(xiàn)簡易放大鏡最全代碼解析(ES5)
- JavaScript實(shí)現(xiàn)簡易輪播圖最全代碼解析(ES6面向?qū)ο?
- JavaScript實(shí)現(xiàn)簡易輪播圖最全代碼解析(ES5)
- JavaScript實(shí)現(xiàn)簡易購物車最全代碼解析(ES6面向?qū)ο?
- 5種方法告訴你如何使JavaScript 代碼庫更干凈
相關(guān)文章
JS中confirm,alert,prompt函數(shù)使用區(qū)別分析
JS中confirm,alert,prompt函數(shù)使用區(qū)別分析,需要的朋友可以參考下。2010-04-04
javascript 最常用的10個(gè)自定義函數(shù)[推薦]
如果不使用類庫或者沒有自己的類庫,儲(chǔ)備一些常用函數(shù)總是有好處的。2009-12-12
ES6 系列之 Generator 的自動(dòng)執(zhí)行的方法示例
這篇文章主要介紹了ES6 系列之 Generator 的自動(dòng)執(zhí)行的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10
利用用JS實(shí)現(xiàn)一個(gè)實(shí)時(shí)小鬧鐘
天我們來聊聊如何使用JS來創(chuàng)建一個(gè)實(shí)時(shí)的小鬧鐘,這個(gè)小鬧鐘十分的有趣,小伙伴們可以運(yùn)行一下,看看跟你電腦上的時(shí)間是否對(duì)的上呢,文章通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11

