前言
为了确保UI的响应性和避免复杂的同步问题,JavaScript被设计为单线程。然而在Web Worker标准中提供了浏览器运行多线程的能力。不过Web Worker运行在全局作用域之外,它们无法访问Window对象和Document对象,和主线程之间通信也只能通过异步消息传递机制实现。
由于原生的Web Worker写法比较复杂,这里我们使用useWorker,一个以React Hooks的方式简单使用Web Worker API的库。
useWorker的Github
安装
npm install --save @koale/useworker
引用
import { useWorker, WORKER_STATUS } from "@koale/useworker";
这是一个很小的库,它只提供了useWokrer这个hooks和worker状态的枚举类。
例子
由于使用比较简单,不多废话直接看代码。(ps: 我是通过create-react-app创建的项目)
import React, {useEffect, useState} from 'react';
import logo from './logo.svg';
import './App.css';
import {useWorker, WORKER_STATUS} from "@koale/useworker";
import toast, {Toaster} from 'react-hot-toast';
let turn = 0;
const infiniteLoop = () => {
const logo = document.querySelector(".App-logo") as HTMLElement;
turn += 8;
logo.style.transform = `rotate(${turn % 360}deg)`;
}
const bubbleSort = (input: number[]) => {
let swap;
let n = input.length - 1;
const sortedArray = input.slice();
do {
swap = false;
for (let index = 0; index < n; index += 1) {
if (sortedArray[index] > sortedArray[index + 1]) {
const tmp = sortedArray[index];
sortedArray[index] = sortedArray[index + 1];
sortedArray[index + 1] = tmp;
swap = true;
}
}
n -= 1;
} while (swap);
return sortedArray;
};
const numbers = [...Array(50000)].map(() =>
Math.floor(Math.random() * 1000000)
);
function App() {
const [sortStatus, setSortStatus] = useState(false);
const [sortWorker, {status: sortWorkerStatus}] = useWorker(bubbleSort);
useEffect(() => {
const loopInterval = setInterval(infiniteLoop, 100);
return () => clearInterval(loopInterval);
}, []);
const onSortClick = () => {
setSortStatus(true);
const result = bubbleSort(numbers);
setSortStatus(false);
toast("Finished: Sort");
console.log("Bubble Sort", result);
};
const onWorkerSortClick = () => {
sortWorker(numbers).then(result => {
toast("Finished: Sort using useWorker.");
console.log("Bubble Sort useWorker()", result);
});
};
return (
<div className="App">
<Toaster/>
<header className="App-header">
<h1 className="App-title">useWorker demo</h1>
<img src={logo} className="App-logo" alt="logo"/>
<div>
<section className="App-section">
<button
type="button"
disabled={sortStatus}
className="App-button"
onClick={() => onSortClick()}
>
{sortStatus ? `Loading...` : `Bubble Sort`}
</button>
<button
type="button"
disabled={sortWorkerStatus === WORKER_STATUS.RUNNING}
className="App-button"
onClick={() => onWorkerSortClick()}
>
{sortWorkerStatus === WORKER_STATUS.RUNNING
? `Loading...`
: `Bubble Sort useWorker()`}
</button>
</section>
</div>
</header>
</div>
);
}
export default App;
效果图如下
这里可以看到,我们先让图标一直转动表示ui主线程一直在执行,当我们不使用Web Worker执行计算任务时很明显会阻塞主线程,而当我们使用Web Worker时并不会影响主线程的执行。