JavaScript实现可终止轮询请求的方法

作者:青柠

时间:2026-10-25

浏览:126

本文详解如何在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轮询请求内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章