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

目录
  • 什么是轮询请求?
  • 轮询的要点
  • setInterval的问题
  • 实现轮询
    • 准备工作
    • 基础版
    • 进阶版
    • 最终版

最近遇到了一个需求,需要每隔5s请求一个接口获取接口返回的结果,返回成功后停止请求,接口的返回的值有下面几种情况:

// 成功
{
    "code": 0,
    "msg": '成功'
}
// 查询中
{
    "code": -1,
    "msg": '结果查询中'
}
// 失败
{
    "code": 1,
    "msg": '返回结果失败'
}

code是接口的状态码,为0的时候表示接口返回的成功,这时就不需要再请求接口。

实现这种需求首先想到的就是用轮询去做了。

什么是轮询请求?

通俗地说,轮询请求就是间隔相同的时间(如5s)后不断地向服务端发起同一个接口的请求,当然不能无限次去请求,所以轮询必须要有个停止轮询的机制

轮询的要点

1. 按照需要选择是否立即发起请求再进入轮询

2. 上一次的请求响应后到了指定的时间间隔后再继续执行下一次请求

3. 当轮询的请求发生报错的时候应该停止轮询

4. 被停止的轮询可以根据需要再次发起轮询

setInterval的问题

因为是不断请求,所以首先能想到的就是用setInterval去实现,但是setInterval做轮询的话,会有以下严重的问题

1. 即使调用的代码报错了,setInterval会持续的调用

2. setInterval不会等到上一次的请求响应后再执行,只要到了指定的时间间隔就会执行,哪怕有报错也会执行

3. setInterval定时不准确,这个跟事件循环有关,这里不展开啦~

实现轮询

实现轮询,只要按照轮询的要点去实现一个setInterval方法就可以了,下面用setTimeout一步步去实现这个方法

准备工作

首先准备一个html模板,这个模板包含两个按钮,一个开启轮询,一个停止轮询,轮询的方法命名为myInterval,返回一个startstop方法用于开始和停止轮询

<!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和错误的时候,中止轮询

最终版

asyncawait可以实现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也是可以中止轮询的

现在一个完美的js可终止轮询请求,interval就完成啦~

到此这篇关于用JavaScript实现可终止的轮询请求的文章就介绍到这了,更多相关js轮询请求内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • js与jQuery终止正在发送的ajax请求的方法

    本文实例讲述了js与jQuery终止正在发送的ajax请求的方法.分享给大家供大家参考,具体如下: 核心:调用XMLHttpRequest对象上的abort方法 jquery的ajax方法有自己的超时时间设置参数: $.ajax({type:'POST', url:'b.php', data:'', timeout:5000, success:function(){ } }) 同时 1. $.get返回的数据类型是XMLHttpRequest,请参考手册.($.post.$.ajax.$.get

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

    目录 什么是轮询请求? 轮询的要点 setInterval的问题 实现轮询 准备工作 基础版 进阶版 最终版 最近遇到了一个需求,需要每隔5s请求一个接口获取接口返回的结果,返回成功后停止请求,接口的返回的值有下面几种情况: // 成功 { "code": 0, "msg": '成功' } // 查询中 { "code": -1, "msg": '结果查询中' } // 失败 { "code": 1, &q

  • JavaScript使用setInterval()函数实现简单轮询操作的方法

    本文实例讲述了JavaScript使用setInterval()函数实现简单轮询操作的方法.分享给大家供大家参考.具体分析如下: 轮询(Polling)是一种CPU决策如何提供周边设备服务的方式,又称"程控输出入"(Programmed I/O).轮询法的概念是,由CPU定时发出询问,依序询问每一个周边设备是否需要其服务,有即给予服务,服务结束后再问下一个周边,接着不断周而复始.轮询法实作容易,但效率偏低. 在JavaScript使用setInterval函数作简单的轮询操作,可以随时

  • Go 实现 Nginx 加权轮询算法的方法步骤

    目录 一,Nginx 负载均衡的轮询 (round-robin) 1. nginx 中的配置 2. 简单介绍 3. 特点 4. 实现 (这里使用golang模拟实现) 5. 测试 二,Nginx 负载均衡的加权轮询 (weighted-round-robin) 1. nginx 配置 2. 加权算法简介-特点 3. 算法说明 4. 简单举例 5. 代码实现 6. 测试验证 最近在看一些 getway 相关的资料,发现有关 Nginx 负载均衡的算法有点多,但是有点乱,所以整理下...如有不对地方

  • vue轮询请求解决方案的完整实例

    轮询的理解 其实轮询的重点在于间隔多少时间执行一次,而并非循环本身.ajax是异步请求,从发起请求到接受到响应即为一个完整的过程,这个过程所需要的时间是无法预料的,说的极端点,若请求所需的时间超过了我们轮询的间隔时间,那么是会出现很多问题的,所以轮询的间隔应该是在确保这个请求过程完成的基础之上的,这也更符合逻辑. 业务描述: 页面初始化显示第一页数据,随后每隔十秒当前页数据刷新 更改筛选条件或者更改页码直接刷新数据,随后每个十秒当前也数据刷新 业务逻辑点分析: 手动调用时,立即执行发起请求 随后

  • Android两种轮询的实现方法

    Android 两种轮询的简单写法,供大家参考,具体内容如下 public void startPolling() { subscriber = Observable.interval(0, POLLING_INTERVAL, TimeUnit.MILLISECONDS). doOnNext(new Action1<Long>() { @Override public void call(Long aLong) { doPolling(). subscribeOn(Schedulers.io(

  • Ajax轮询请求状态(微信公众号带参数二维码登录网站)

    这里要实现的功能是:通过扫码微信公众号带参数的二维码,来登录网站. 但很明显,如果ajax不间断的请求服务器,这样会加重服务器的负荷,所以本例采用的是js的setInterval来周期性调用执行一个ajax函数来来向服务器请求数据,但请求成功或者请求一定次数后还未成功时用clearinterval函数清空计时器. 代码和注释如下:(后端采用thinkPHP实现,所以js代码中含有一些thinkPHP的语法规则) <script type="text/javascript" src

  • Java 轮询锁使用时遇到问题解决方案

    目录 问题演示 简易版轮询锁 问题1:死循环 反例 优化版 问题2:线程饿死 反例 优化版 总结 前言: 当我们遇到死锁之后,除了可以手动重启程序解决之外,还可以考虑使用顺序锁和轮询锁,这部分的内容可以参考上一篇文章Java 死锁解决方案顺序锁和轮询锁,这里就不再赘述了.然而,轮询锁在使用的过程中,如果使用不当会带来新的严重问题,所以本篇我们就来了解一下这些问题,以及相应的解决方案. 问题演示 当我们没有使用轮询锁之前,可能会出现这样的问题: import java.util.concurren

  • Java实现一个简单的长轮询的示例代码

    目录 分析一下长轮询的实现方式 长轮询与短轮询 配置中心长轮询设计 配置中心长轮询实现 客户端实现 服务端实现 分析一下长轮询的实现方式 现在各大中间件都使用了长轮询的数据交互方式,目前比较流行的例如Nacos的配置中心,RocketMQ Pull(拉模式)消息等,它们都是采用了长轮询方的式实现.就例如Nacos的配置中心,如何做到服务端感知配置变化实时推送给客户端的呢? 长轮询与短轮询 说到长轮询,肯定存在和它相对立的,我们暂且叫它短轮询吧,我们简单介绍一下短轮询: 短轮询也是拉模式.是指不管

  • c# 实现轮询算法实例代码

    c# 轮询算法 这两天做东西,业务上有个特殊的需求,在用户访问页面的时候,针对某一行代码进行控制,按照概率来进行显示,我做的是针对当前页面的曝光进行处理,曝光代码是第三方的,页面上只要有这段代码就算是执行了这段曝光代码,所以才写了这个轮询的一个方法,这个方法可以根据自己的需求修改,下面我把这个方法全部帖出来: CacheSlidingExpirationHour:时间,缓存时间2小时 CountdownCurrentIndexCacheName:缓存名称 log:日志 m_objCountdow

  • javascript和jQuery实现网页实时聊天的ajax长轮询

    介绍 大家都知道,HTTP协议是一个属于应用层的面向对象的协议,HTTP 协议一共有五大特点: 1.支持客户/服务器模式; 2.简单快速; 3.灵活; 4.无连接; 5.无状态. 所以一次的请求都是一个单独的事件,和前后都没有联系.所以我们在解决网页实时聊天时就遇到一个问题,如何保证与服务器的长时间联系,从而源源不段地获取信息. 一直以来的方式无非有这么几种: 1.长连接,即服务器端不断开联系,PHP服务器端用ob系列函数来不停的读取输出,但是相当耗费服务器资源. 2.Flash socket,

随机推荐