前端交互体验优化-空闲时执行计算任务

我们知道,js 是单线程的脚本语言,单线程的特点就是所有代码只能按照线性先后顺序,同步的被执行。

即使我们说 js 可以执行异步任务,但本质是这些异步任务只是放在了等候队列,依然需要等待同步队列执行完成后,才能被加入到同步队列中,同步的去执行。

因此即使将计算任务放到异步队列中,它最终依然会被同步的执行,那么整个计算过程就会阻塞主线程的执行。

比如这样一个场景:点击"开始计算"按钮 执行1000*1000次的打印任务,紧接着点击"计时器"按钮

xml 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>同步执行计算任务</title>
  <style>
    html, body {
      text-align: center;
      height: 100%;
    }
    body {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
    }
  </style>
</head>

<body>

  <button id="clickMe">开始计算</button>
  <br />
  <button id="counter" style="margin-top: 20px;">Counter: 0</button>
  <script>
    const clickBtn = document.getElementById('clickMe');
    const counterBtn = document.getElementById('counter');

    let counter = 0;

    clickBtn.addEventListener('click', () => {
      calc();
    });

    counterBtn.addEventListener('click', () => {
      counter ++;
      console.log(`%cCounter Update: ${counter}`, 'color: green;');
      counterBtn.innerText = `Counter: ${counter}`
    });
    function calc() {
      for (let i = 0; i < 1000; i++) {
        console.log(`%cOuter Calc: ${i}`, 'color: gray;');
        for (let j = 0; j < 1000; j++) {
          console.debug(`${i}-${j}`);
        }
      }
    }
  </script>
</body>

</html>

在执行计算任务的过程中,会发现点击"计时器"按钮的事件长时间得不到响应,整个js线程都被阻塞在执行中的计算任务,也就是我们所说的卡死状态。直到任务计算完成,卡死状态才会结束,事件才会得到响应。

如果真实业务中存在这种大量的计算,且实现方式跟上面一样,可想而知,交互体验将会变的很糟糕!

假如我们的计算任务不能通过算法进行提效,那么应该怎么解决因大量计算导致的交互卡顿或卡死的问题呢?

下面有几种方案可供参考:

  1. 使用 webworker,开启子线程执行计算任务

  2. 使用requestIdleCallback 在主线程中,进行空闲时分片计算

  3. 如果业务可行,将计算任务交给其他端(如:BS架构的服务端)

webworker 虽然可以多线程并发,但是跨线程数据传输的性能损耗和瓶颈,对于数据量大的时候,带来的问题也会很明显。

我们这里主要分析,计算任务必须放在前端处理的场景,所以会采用方案:requestIdleCallback 空闲时分片计算进行举例。

依然是上面的例子,我们使用requestIdleCallback 空闲时接口进行改造

xml 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>空闲时-分片执行计算任务</title>
  <style>
    html, body {
      text-align: center;
      height: 100%;
    }
    body {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
    }
  </style>
</head>

<body>

  <button id="clickMe">开始计算</button>
  <br />
  <button id="counter" style="margin-top: 20px;">Counter: 0</button>
  <script>
    const clickBtn = document.getElementById('clickMe');
    const counterBtn = document.getElementById('counter');

    let counter = 0;

    clickBtn.addEventListener('click', () => {
      calc();
    });

    counterBtn.addEventListener('click', () => {
      counter ++;
      console.log(`%cCounter Update:${counter}`, 'color: green;');
      counterBtn.innerText = `Counter: ${counter}`
    });
    
    function calc() {
      let index = 0;
        
      // 将大的计算任务,拆分为小的计算任务,通过空闲时去分片执行
      function innerCalc(i) {
        console.log(`%cOuter Calc: ${i}`, 'color: gray;');
        for (let j = 0; j < 1000; j++) {
          console.debug(`${i}-${j}`);
        }
      }
      
      function outerCalc(deadline) {
        // 如果有空闲时间,或者计算等待超时,则执行计算任务
        while (index < 1000 && (deadline.timeRemaining() > 0 || deadline.timeout)) {
          innerCalc(index);
          index ++;
        }
        if (index < 1000) {
          requestIdleCallback(outerCalc, {timeout: 1000});
        } else {
          console.log("任务执行结束");
        }
      }
      requestIdleCallback(outerCalc, { timeout: 1000 });
    }
  </script>
</body>

</html>

可以看出来,当我们使用空闲时计算时,虽然计算任务一直在持续,但是并没有阻塞 "计时器" 的交互和更新。这会给交互体验,带来很大的改善。

使用空闲时执行计算任务,实现交互体验优化的关键有两个点:

  • 将计算任务的优先级降低,加入异步等待队列
  • 将大的计算任务拆分为小的任务,分片执行,当任务被执行时,不会长时间用户交互行为
相关推荐
OEC小胖胖3 分钟前
09|DOM Renderer 的 Host 层:从 Fiber 到真实 DOM 的落地
前端·前端框架·react·开源库
xuyuan19983 分钟前
超越Selenium:自动化测试框架Cypress在现代前端测试中的卓越实践(windows版本)三
前端·windows·测试工具·系统架构·cypress
企业对冲系统官6 分钟前
价格风险管理平台审批角色配置与权限矩阵设计
大数据·运维·开发语言·前端·网络·数据库·矩阵
步步为营DotNet7 分钟前
深度剖析.NET 中CancellationToken:精准控制异步操作的关键
java·前端·.net
thinkQuadratic8 分钟前
CSS给文本添加背景颜色等效果
前端·css
波波鱼દ ᵕ̈ ૩9 分钟前
AJAX(1)
前端·javascript·ajax
毕设十刻10 分钟前
基于Vue的酒店管理系统4yv4w(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
梦65011 分钟前
Vue3 响应式原理与响应式属性 详解
前端·javascript·vue.js
摘星编程12 分钟前
React Native for OpenHarmony 实战:Clipboard 剪贴板详解
javascript·react native·react.js
程序员的程15 分钟前
我用 stock-sdk 做了个 A 股股票看板
前端·javascript·typescript