Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[历史博客]如何在一个React应用中使用Web Worker #52

Open
z0nka1 opened this issue Feb 7, 2021 · 0 comments
Open

[历史博客]如何在一个React应用中使用Web Worker #52

z0nka1 opened this issue Feb 7, 2021 · 0 comments

Comments

@z0nka1
Copy link
Owner

z0nka1 commented Feb 7, 2021

来自我的早期博客,出处:https://z0nka1.github.io/

今天闲着没事,于是想试一下在React应用中使用Web Worker,用来测试Web Worker运行在单独的线程中,不影响主线程。

于是我用Create React App生成了一个React初始工程,然后在src目录添加了一个文件worker.js

let i = 0;
while (i < 200000) {
  postMessage("Web Worker Counter: " + i);
  i++;
}

接着,我在App.js写了一些简单的代码:

import React, { useState } from "react";
import "./App.css";

let worker;

function App() {
  const [workerOutput, setWorkerOutput] = useState("");
  const [mainThreadOutput, setMainThreadOutput] = useState("");

  const testWorker = () => {
    if (typeof Worker !== "undefined") {
      if (typeof worker === "undefined") {
        worker = new Worker('./worker.js');
      }
      worker.onmessage = function (event) {
        setWorkerOutput(event.data);
      };
    } else {
      setWorkerOutput("Web Workers are not supported in your browser");
    }
  };

  const terminateWorker = () => {
    worker && worker.terminate();
    worker = undefined;
  };

  const testMainThread = () => {
    for (let i = 0; i < 2000000; i++) {
      setMainThreadOutput("Main Thread Counter: " + i);
    }
  };

  return (
    <div className="App">
      <div className="output-cont">
        <button onClick={testWorker}>start worker</button>
        <h3 id="workerOutput">{workerOutput}</h3>
        <button onClick={terminateWorker}>terminate worker</button>
      </div>
      <br />
      <div className="output-cont">
        <button onClick={testMainThread}>start blocking main thread</button>
        <h3 id="mainThreadOutput">{mainThreadOutput}</h3>
      </div>
      <br />
      <div className="output-cont">
        <button
          onClick={() => {
            alert("browser responsive!");
          }}
        >
          test browser responsiveness
        </button>
      </div>
    </div>
  );
}

export default App;

我想测试当Web Worker中执行一些繁重的计算工作的时候,用户依然可以和页面进行交互,比如响应用户点击事件。

一切看起来都很完美!

但是当我点击start worker按钮的时候,浏览器报告了一个错误:

Uncaught SyntaxError: Unexpected token '<'

于是我去网上寻找解决方案,最终在Create React App的GitHub仓库找到了方法

照着他的方法,我把代码改成了这样:

const workerCode = () => {
  let i = 0;
  while (i < 200000) {
    postMessage("Web Worker Counter: " + i);
    i++;
  }
};

let code = workerCode.toString();
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));

const blob = new Blob([code], { type: 'application/javascript'});
const worker_script = URL.createObjectURL(blob);

module.exports = worker_script;
import React, { useState } from "react";
import "./App.css";
import worker_script from './worker'; <--- 导入

let worker;

function App() {
  const [workerOutput, setWorkerOutput] = useState("");
  const [mainThreadOutput, setMainThreadOutput] = useState("");

  const testWorker = () => {
    if (typeof Worker !== "undefined") {
      if (typeof worker === "undefined") {
        worker = new Worker(worker_script); <--- 使用
      }
      worker.onmessage = function (event) {
        setWorkerOutput(event.data);
      };
    } else {
      setWorkerOutput("Web Workers are not supported in your browser");
    }
  };

  ...剩余代码
}

export default App;

再次运行程序,这一次终于正常了。

让我们逐步看一下做了哪些修改:

const workerCode = () => {
  let i = 0;
  while (i < 200000) {
    postMessage("Web Worker Counter: " + i);
    i++;
  }
};

↑首先把要在Worker中执行的代码放到一个函数里面。

let code = workerCode.toString();

↑接着把函数转为字符串。

code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));

↑然后截取函数体部分。

const blob = new Blob([code], { type: 'application/javascript'});

↑把这部分字符串转为Blob对象。

const worker_script = URL.createObjectURL(blob);

↑再把这个Blob对象转为DOMString

这样,就可以在别的文件通过import来访问这个文件对象了:

import worker_script from './worker';

由于本人水平有限,对这里面的具体技术细节还不是很了解,等我熟悉这里面的细节,我会回来更新这篇文章。

感谢阅读!

@z0nka1 z0nka1 changed the title 如何在一个React应用中使用Web Worker [历史博客]如何在一个React应用中使用Web Worker Feb 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant