前端长任务

Js 是单线程的语言。如果一段 js 代码的逻辑占用了大量 CPU,那么就会造成「阻塞」,从而导致后面的渲染逻辑迟迟无法执行。 一般来说,超过 50ms 的任务,就是「长任务」。

长任务优化 1: 使用 Web Worker

对于长任务,在浏览器环境下,可以使用 Worker 规范,来开启子线程专门用来计算,主线程只负责发起计算任务和读取计算结果。 在 nodejs 中,可以通过 child_processworker_thread 来开启多进程或者工作线程,达到类似的效果。

假设现在要实现一个长任务计算,主线程的整体流程是:

  1. 主线程加载 worker 线程
  2. 主线程向 worker 线程发送数据,告诉 worker 线程开始计算任务
  3. 主线程监听 worker 线程返回的数据,然后根据具体业务逻辑使用它

主线程代码如下:

/*
 * @Author: dongyuanxin
 * @Date: 2021-01-11 23:29:13
 * @Github: <https://github.com/dongyuanxin/blog>
 * @Blog: <https://xin-tan.com/>
 * @Description: 长任务优化-worker
 */
import ReactDom from "react-dom";
import React, { useEffect } from "react";

const App = () => {
    useEffect(() => {
        // 开启一个worker线程
        const worker = new Worker("./6.worker.js");
        // 监听worker线程传来的消息
        worker.addEventListener("message", (e) => {
            console.log(`>>> [main.js] worker's data is:`, e.data);
        });
        // 监听worker线程的报错
        worker.addEventListener("messageerror", (e) => {
            console.log(`>>> [main.js] worker's error is:`, e.data);
        });
        // 将任务交给worker线程执行
        console.log(">>> [main.js] 开始进行任务计算");
        worker.postMessage(1000000n);
        console.log(">>> [main.js] 交给Worker处理");
    }, []);

    return <span>App</span>;
};

const rootElement = document.getElementById("root");
ReactDom.render(<App />, rootElement);

worker 线程的整体流程是:

  1. 监听主线程发送数据,收到数据时,启动计算任务
  2. 计算完后,将任务结果发送给主线程

worker 线程代码如下:

// 在worker线程中,self的作用类似window
// 监听主线程传来的消息
self.addEventListener("message", (e) => {
    // 执行计算任务
    console.log(`>>> [worker.js] main's data is:`, e.data);
    for (let i = 0n; i < e.data; i = i + 1n) {
        // 模拟浪费CPU的计算
    }
    // 将计算结果返回给主线程
    self.postMessage(e.data * 2n);
});

长任务优化 2: 使用 Generator Function

对于长任务来说,WebWorker 本质上是使用多线程。还有其它方法吗?有,可以尝试将长任务分解成短任务。

这里「分解成短任务」,不是说将其拆分为多个子函数,因为这样还是一次性都要执行,由于函数堆栈调用,时间甚至更慢。而是说,让长任务运行到某个部分,然后暂停,空出 CPU 去执行其它任务,例如渲染界面、响应用户交互。之后,再拿回 CPU,继续上次的计算。

这种处理思路,有点像早期操作系统,只有一个进程。那怎么让用户看起来所有的任务都在执行呢?只能来回切换任务,但同一时刻,其实只有一个任务在执行;而多进程,是同一时刻,多个任务同时进行。也就是并发和并行的区别。

Powered by Fruition