• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

前端异步请求轮询方案

武飞扬头像
明里人
帮助1

业务背景

在前后端数据交互场景下,使用最多的一种方式是客户端发起 HTTP 请求,等待服务端处理完成后响应给客户端结果。

但在一些场景下,服务端对数据的处理需要较长的时间,比如提交一批数据,对这批数据进行数据分析,将最终分析结果返回给前端。

如果采用一次 HTTP 请求,用户会一直处于等待状态,再加上界面不会有进度交互,导致用户不知何时会处理完成;此外,一旦刷新页面或者其他意外情况,用户就无从感知处理结果。

面对这类场景,可以借助 「HTTP 轮询方式」 对交互体验进行优化,具体过程如下:

首先发起一次 HTTP 请求用于提交数据,之后启动轮询在一定间隔时间内查询分析结果,在这期间后台可将分析进度同步到前端来告知用户处理进度;此外即使刷新再次进入页面还可以通过「轮询」实时查询进度结果。

下面,我们来看看代码层面看如何实现这类场景。

JS 实现轮询的方式

在实现代码之前,我们需要先明确 JS 实现轮询的方式有哪些,哪种方式最适合使用。

1. setInterval

作为前端开发人员,提起轮询第一时间能想到的是计时器 setInterval,它会按照指定的时间间隔不间断的轮询执行处理函数。

let index = 1;

setInterval(() => {
  console.log('轮询执行: ', index   );
}, 1000);

回过头来看我们的场景:要轮询的是 异步请求(HTTP),请求响应结果会受限制网络或者服务器处理速度,显然 setInterval 这种固定间隔轮询并不适合这个场景。

2. Promise setTimeout sleep

setInterval 的不足之处在于 轮询间隔时间 在异步请求场景下无法保证两个请求之间的间隔固定。要解决这个问题,可以使用 sleep 睡眠函数来控制间隔时间。

JS 中没有提供 sleep 相关方法,但可以结合 Promise setTimeout 来实现。

const sleep = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 1000);
  });
}

sleep 仅控制了轮询间隔,而轮询的执行机制需要我们手动根据异步请求结果来实现,比如下面通过控制 while 循环的条件:

const start = async () => {
  let i = 0;
  while (i < 5) {
    await sleep();
    console.log(`第 ${   i} 次执行`);
  }
}

start();

使用轮询的时候可以借助 async/await 同步的方式编写,提高代码阅读质量。

实现异步请求轮询

下面我们通过一个完整示例理解 轮询异步请求 的实现及使用注意事项。

首先我们定义两个变量:index 用于控制何时停止轮询,timer 则用于实现中断轮询。

let index = 1;
let timer = 0;

这里,我们定义 syncPromise 来模拟异步请求,可以看作是一次 HTTP 请求,当进行 5 次异步请求后,会返回 false 表示拿到数据分析结果,停止数据查询轮询:

const syncPromise = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`第 ${index} 次请求`);
      resolve(index < 5 ? true : false);
      index   ;
    }, 50);
  })
}

现在,我们实现 pollingPromise 作为 sleep 睡眠函数使用,去控制轮询的间隔时间,并在指定时间执行异步请求:

const pollingPromise = () => {
  return new Promise(resolve => {
    timer = setTimeout(async () => {
      const result = await syncPromise();
      resolve(result);
    }, 1000);
  });
}

最后,startPolling 作为开始轮询的入口,包含以下逻辑:

  • 1)在轮询前会清除正在进行的轮询任务,避免出现多次轮询;
  • 2)如果需要,在开始轮询时会立刻调用异步请求查询一次数据结果;
  • 3)最后,通过 while 循环根据异步请求的结果,决定是否继续轮询;
const startPolling = async () => {
  // 清除进行中的轮询,重新开启计时轮询
  clearTimeout(timer); // !!! 注意:清除计时器后,会导致整个 async/await 链路中断,若计时器的位置下方还存在代码,将不会执行。
  index = 1;
  // 立刻执行一次异步请求
  let needPolling = await syncPromise();
  // 根据异步请求结果,判断是否需要开启计时轮询
  while (needPolling) {
    needPolling = await pollingPromise();
  }
  console.log('轮询请求处理完成!'); // 若异步请求被 clearTimeout(timer),这里不会被执行打印输出。
}

const start = async () => {
  await startPolling();
  console.log('若异步请求被 clearTimeout(timer),这里将不会被执行');
}
start();

不过,需要注意的是:一旦清除计时器后,会导致整个 async/await 链路中断,若计时器的位置下方还存在代码,将不会执行。

假设当前执行了两次轮询被 clearTimeout(timer) 后,从 startPollingstart 整个 async/await 链路都会中断,且后面未执行的代码也不会被执行。

基于以上规则,异步轮询的处理逻辑尽量放在 syncPromise 异步请求核心函数中完成,避免在开启轮询的辅助函数中去实现。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgahakj
系列文章
更多 icon
同类精品
更多 icon
继续加载