JavaScript實(shí)現(xiàn)可終止輪詢請(qǐng)求的方法
最近遇到了一個(gè)需求,需要每隔5s請(qǐng)求一個(gè)接口獲取接口返回的結(jié)果,返回成功后停止請(qǐng)求,接口的返回的值有下面幾種情況:
// 成功
{
"code": 0,
"msg": '成功'
}
// 查詢中
{
"code": -1,
"msg": '結(jié)果查詢中'
}
// 失敗
{
"code": 1,
"msg": '返回結(jié)果失敗'
}code是接口的狀態(tài)碼,為0的時(shí)候表示接口返回的成功,這時(shí)就不需要再請(qǐng)求接口。
實(shí)現(xiàn)這種需求首先想到的就是用輪詢?nèi)プ隽恕?/p>
什么是輪詢請(qǐng)求?
通俗地說,輪詢請(qǐng)求就是間隔相同的時(shí)間(如5s)后不斷地向服務(wù)端發(fā)起同一個(gè)接口的請(qǐng)求,當(dāng)然不能無限次去請(qǐng)求,所以輪詢必須要有個(gè)停止輪詢的機(jī)制
輪詢的要點(diǎn)
1. 按照需要選擇是否立即發(fā)起請(qǐng)求再進(jìn)入輪詢
2. 上一次的請(qǐng)求響應(yīng)后到了指定的時(shí)間間隔后再繼續(xù)執(zhí)行下一次請(qǐng)求
3. 當(dāng)輪詢的請(qǐng)求發(fā)生報(bào)錯(cuò)的時(shí)候應(yīng)該停止輪詢
4. 被停止的輪詢可以根據(jù)需要再次發(fā)起輪詢
setInterval的問題
因?yàn)槭遣粩嗾?qǐng)求,所以首先能想到的就是用setInterval去實(shí)現(xiàn),但是setInterval做輪詢的話,會(huì)有以下嚴(yán)重的問題
1. 即使調(diào)用的代碼報(bào)錯(cuò)了,setInterval會(huì)持續(xù)的調(diào)用
2. setInterval不會(huì)等到上一次的請(qǐng)求響應(yīng)后再執(zhí)行,只要到了指定的時(shí)間間隔就會(huì)執(zhí)行,哪怕有報(bào)錯(cuò)也會(huì)執(zhí)行
3. setInterval定時(shí)不準(zhǔn)確,這個(gè)跟事件循環(huán)有關(guān),這里不展開啦~
實(shí)現(xiàn)輪詢
實(shí)現(xiàn)輪詢,只要按照輪詢的要點(diǎn)去實(shí)現(xiàn)一個(gè)setInterval方法就可以了,下面用setTimeout一步步去實(shí)現(xiàn)這個(gè)方法
準(zhǔn)備工作
首先準(zhǔn)備一個(gè)html模板,這個(gè)模板包含兩個(gè)按鈕,一個(gè)開啟輪詢,一個(gè)停止輪詢,輪詢的方法命名為myInterval,返回一個(gè)start和stop方法用于開始和停止輪詢
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="start">開始</button>
<button id="stop">停止</button>
</body>
</html>
<script>
const startBtn = document.getElementById('start')
const stopBtn = document.getElementById('stop')
// 輪詢?cè)谶@
function myInterval() {
return {
start: () => {},
stop: () => {}
}
}
// 輪詢管理器
const intervalManager = myInterval(main)
// 輪詢的方法
let count = 0
function main() {
count += 1
console.log('執(zhí)行:', count)
}
startBtn.addEventListener('click', intervalManager.start)
stopBtn.addEventListener('click', intervalManager.stop)
</script>基礎(chǔ)版
function myInterval(callback, interval = 2000) {
let timerId
const loop = async () => {
callback()
return (timer = setTimeout(loop, interval))
}
return {
start: () => {
loop()
},
stop: () => {
console.log('停止執(zhí)行')
clearTimeout(timerId)
}
}
}
這個(gè)版本基本跟setInterval的功能一致了,只不過執(zhí)行需要手動(dòng)調(diào)用start,停止要調(diào)用stop。
一個(gè)常見的場景是,當(dāng)輪詢執(zhí)行n次后,停止輪詢,下面改一下main方法,等count等于5的時(shí)候停止輪詢
function main() {
count += 1
console.log('執(zhí)行:', count)
if (count == 5) {
count = 0
intervalManager.stop()
}
}
發(fā)現(xiàn)當(dāng)count等于5時(shí),確實(shí)調(diào)用了stop方法,但是卻沒有停止輪詢,當(dāng)點(diǎn)擊停止按鈕的時(shí)候,又停止了輪詢,這是什么情況呢?
看一下loop方法
const loop = async () => {
callback()
return (timerId = setTimeout(() => {
loop()
}, interval))
}因?yàn)槭窃?code>callback中執(zhí)行stop的,stop并沒有阻止定時(shí)器中回調(diào)(loop)的執(zhí)行,所以看起來是停止了,但是新的定時(shí)器還是開啟了
進(jìn)階版
解決基礎(chǔ)版無法自動(dòng)停止的問題也很簡單,加一個(gè)變量來檢測,一旦執(zhí)行了stop方法不返回定時(shí)器就可以了
// 可以自動(dòng)停止的定時(shí)器
function myInterval(callback, interval = 2000) {
let timer
let isStop = false
const stop = () => {
console.log('停止')
isStop = true
clearTimeout(timer)
}
const start = () => {
isStop = false
loop()
}
const loop = async () => {
callback()
if (isStop) return
return (timer = setTimeout(loop, interval))
}
return {
start,
stop
}
}
下面把main方法再改一改
function main() {
const flag = parseInt(Math.random() * 2) === 1
console.log('flag', flag)
return flag ? Promise.resolve() : Promise.reject()
}main方法這次返回的是Promise,我們來看看執(zhí)行的情況

進(jìn)階版還是不支持main中有Promise的情況,我們希望的是,當(dāng)main中出現(xiàn)Promise reject和錯(cuò)誤的時(shí)候,中止輪詢
最終版
用async 和 await可以實(shí)現(xiàn)main方法中出現(xiàn)錯(cuò)誤的時(shí)候中止輪詢
function myInterval(callback, interval = 2000) {
let timer
let isStop = false
const stop = () => {
console.log('停止')
isStop = true
clearTimeout(timer)
}
const start = async () => {
isStop = false
await loop()
}
const loop = async () => {
try {
await callback(stop)
} catch (err) {
console.error('輪詢出錯(cuò):', err)
throw new Error('輪詢出錯(cuò):', err)
}
if (isStop) return
return (timer = setTimeout(loop, interval))
}
return {
start,
stop
}
}
可以看到最終版能滿足我們的輪詢要求了,遇到接口報(bào)錯(cuò)的時(shí)候可以終止請(qǐng)求,細(xì)心的您應(yīng)該能發(fā)現(xiàn)這行代碼 callback(stop),是的,main中現(xiàn)在多了一個(gè)回調(diào)方法,可以直接調(diào)用該方法停止輪詢
把main再次改回來
let count = 0
function main(stop) {
count += 1
console.log('count:', count)
if (count == 5) {
stop()
}
}
可以看到,調(diào)用stop也是可以中止輪詢的
現(xiàn)在一個(gè)完美的js可終止輪詢請(qǐng)求,interval就完成啦~
到此這篇關(guān)于用JavaScript實(shí)現(xiàn)可終止的輪詢請(qǐng)求的文章就介紹到這了,更多相關(guān)js輪詢請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS?new操作原理及手寫函數(shù)模擬實(shí)現(xiàn)示例
這篇文章主要為大家介紹了JS?new操作原理及手寫函數(shù)模擬實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
JS小功能(操作Table--動(dòng)態(tài)添加刪除表格及數(shù)據(jù))實(shí)現(xiàn)代碼
這篇文章主要介紹了操作Table--動(dòng)態(tài)添加刪除表格及數(shù)據(jù)實(shí)現(xiàn)代碼,有需要的朋友可以參考一下2013-11-11
微信小程序如何根據(jù)不同用戶切換不同TabBar(簡單易懂!)
小程序中我們可能需要根據(jù)不同的權(quán)限展示不同的tabbar,下面這篇文章主要給大家介紹了關(guān)于微信小程序如何根據(jù)不同用戶切換不同TabBar的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
深入講解xhr(XMLHttpRequest)/jsonp請(qǐng)求之a(chǎn)bort
這篇文章主要給大家深入的介紹了關(guān)于xhr(XMLHttpRequest)/jsonp請(qǐng)求之a(chǎn)bort的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-07-07
three.js實(shí)現(xiàn)3D影院的原理的代碼分析
本篇文章主要給大家講解了如何通過three.js實(shí)現(xiàn)3D影院的功能以及原理分析,需要的朋友參考一下吧。2017-12-12
JS如何在不同平臺(tái)實(shí)現(xiàn)多語言方式
這篇文章主要介紹了JS如何在不同平臺(tái)實(shí)現(xiàn)多語言方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

