JavaScript实现可终止轮询请求的方法
本文详解如何在JavaScript中实现可终止的轮询请求。通过对比setInterval的缺陷,逐步构建基于setTimeout的轮询函数,解决自动停止、Promise错误处理及回调传递等问题,最终实现一个支持手动停止、自动失败终止且可重复使用的完美轮询方案。
最近遇到一个业务需求,需要每隔5秒请求一次接口以获取结果。当接口返回成功状态时,应停止继续请求。该接口返回的数据包含以下几种状态:
// 成功
{
"code": 0,
"msg": '成功'
}
// 查询中
{
"code": -1,
"msg": '结果查询中'
}
// 失败
{
"code": 1,
"msg": '返回结果失败'
}code表示接口的状态码,当其值为0时,代表接口请求成功,此时无需再进行后续请求。
面对此类需求,最直接的想法是使用轮询机制来实现。
什么是轮询请求?
简单来说,轮询请求是指以固定的时间间隔(例如5秒)不断向服务器发起相同的接口请求。由于不能无限次请求,因此轮询机制必须包含停止轮询的逻辑。
轮询的关键点
1. 按照需要选择是否立即发起请求再进入轮询
2. 上一次的请求响应后到了指定的时间间隔后再继续执行下一次请求
3. 当轮询的请求发生报错的时候应该停止轮询
4. 被停止的轮询可以根据需要再次发起轮询
setInterval存在的问题
由于需要不断发起请求,首先想到的实现方式可能是使用setInterval。然而,使用setInterval进行轮询会引发以下严重问题:
1. 即使调用的代码报错了,setInterval会持续的调用
2. setInterval不会等到上一次的请求响应后再执行,只要到了指定的时间间隔就会执行,哪怕有报错也会执行
3. setInterval定时不准确,这个跟事件循环有关,这里不展开啦~
实现轮询逻辑
要实现轮询,只需按照上述要点封装一个类似setInterval的方法即可。下面将使用setTimeout逐步实现该方法。
准备工作
首先准备一个HTML模板,其中包含两个按钮:一个用于开启轮询,另一个用于停止轮询。轮询方法命名为myInterval,该方法返回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')
// 轮询在这
function myInterval() {
return {
start: () => {},
stop: () => {}
}
}
// 轮询管理器
const intervalManager = myInterval(main)
// 轮询的方法
let count = 0
function main() {
count += 1
console.log('执行:', count)
}
startBtn.addEventListener('click', intervalManager.start)
stopBtn.addEventListener('click', intervalManager.stop)
</script>基础版实现
function myInterval(callback, interval = 2000) {
let timerId
const loop = async () => {
callback()
return (timer = setTimeout(loop, interval))
}
return {
start: () => {
loop()
},
stop: () => {
console.log('停止执行')
clearTimeout(timerId)
}
}
}
这个版本的功能基本与setInterval一致,不同之处在于执行时需要手动调用start方法,停止时需调用stop方法。
常见场景是,当轮询执行n次后停止。下面修改main方法,当count等于5时停止轮询。
function main() {
count += 1
console.log('执行:', count)
if (count == 5) {
count = 0
intervalManager.stop()
}
}
发现当count等于5时,确实调用了stop方法,但轮询并未立即停止。当点击停止按钮时,轮询才停止。这是怎么回事呢?
查看loop方法的实现。
const loop = async () => {
callback()
return (timerId = setTimeout(() => {
loop()
}, interval))
}因为在callback中执行了stop,但stop并没有阻止定时器中回调(loop)的执行,所以看起来似乎停止了,但实际上新的定时器依然会被开启。
进阶版实现
解决基础版无法自动停止的问题很简单,只需增加一个变量进行检测。一旦执行了stop方法,就不再返回定时器即可。
// 可以自动停止的定时器
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,我们来看看执行效果。

进阶版仍然不支持main中包含Promise的情况。我们希望当main中出现Promise reject或错误时,能够中止轮询。
最终版实现
使用async和await可以实现当main方法中出现错误时中止轮询。
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('轮询出错:', err)
throw new Error('轮询出错:', err)
}
if (isStop) return
return (timer = setTimeout(loop, interval))
}
return {
start,
stop
}
}
可以看到,最终版满足了我们的轮询要求,遇到接口报错时可以终止请求。细心的读者应该能发现这行代码callback(stop),是的,main中现在多了一个回调方法,可以直接调用该方法来停止轮询。
将main再次改回之前的形式。
let count = 0
function main(stop) {
count += 1
console.log('count:', count)
if (count == 5) {
stop()
}
}
可以看到,调用stop也可以中止轮询。
现在,一个完美的JavaScript可终止轮询请求函数interval就完成啦。
到此这篇关于用JavaScript实现可终止的轮询请求的文章就介绍到这了,更多相关js轮询请求内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
