关于Web Workers

548 阅读2分钟

概述

js采用的是单线程模型,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着,而遇到异步耗时操作的逻辑的时候,一般会使用到JavaScript的异步机制,通过事件循环加回调函数的方式来处理耗时操作,但是遇到类似Json.parse()这种同步代码操作的时候,就同样会阻塞后续代码的执行,假设后端返回了一个特别大的json数据,我们前端获取到数据的时候,一般会json.parse进行序列化,但是大数据意味着执行相关转化逻辑的时候就会阻塞代码的执行,那么这样的耗时操作我们就可以使用到webwork了。

web work的理解

我的理解其实是:webwork就是为js创造多线程的环境,允许主线程创建webwork线程,将未处理的一些任务分给后者 运行.在js主线程运行的同时,work线程在后台运行,两者互不打扰,等到webwork线程的任务结束后,通过发布订阅的模式将消息发布给订阅者。

使用方法

编写computed.js 该线程执行一段计算,耗时特别长。

//webwork线程
let count = 0;
for (let i = 0; i < 10000000000; i++) {
  count += i;
}
//这里线程不能访问window对象,只有仅有的部分api放到,不能操作dom,线程之间通过postMessage传递信息
postMessage({
  count
});

编写index.html 我们将很大计算量的工作使用webwok开启的线程进行处理,然后计算完成后通知主线程进行另外的路径处理。避免主线程代码执行阻塞。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
</body>

<script>
    let work = new Worker("./computed.js");
    work.onmessage = (e) => {
    //大概一分钟后计算出结果,这里取到结果
        console.log(e)
    }
    
    console.log("主线程")
</script>

</html>

webwork如何订阅主线程发过来的消息呢 更改computed.js

self.onmessage = (e) => {
  console.log("work message", e);
};

更改index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="text">
</body>

<script>
    let work = new Worker("./computed.js");
    let input = document.querySelector("input")
    work.onmessage = (e) => {
        console.log(e);
    }
    work.postMessage("hah1")
    
    //新增
    input.oninput = (e) => {
        work.postMessage(e.target.value)
    }
</script>

</html>

在webpack5环境下配置

从 webpack 5 开始,你可以使用 Web Workers 代替 worker-loader

语法

new Worker(new URL('./worker.js', import.meta.url));

选择这种语法是为了实现不使用 bundler 就可以运行代码,它也可以在浏览器中的原生 ECMAScript 模块中使用。

请注意,虽然 Worker API 建议 Worker 构造函数接受表示 URL 的字符串脚本,在 webpack 5 中你只能使用 URL 代替。

示例

src/index.js

const worker = new Worker(new URL('./deep-thought.js', import.meta.url));
worker.postMessage({
  question:
    'The Answer to the Ultimate Question of Life, The Universe, and Everything.',
});
worker.onmessage = ({ data: { answer } }) => {
  console.log(answer);
};

src/deep-thought.js

self.onmessage = ({ data: { question } }) => {
  self.postMessage({
    answer: 42,
  });
};

vue-cli环境下使用

假设我们有个网络请求需要耗时很久,并且解析json也需要很久,这时候可以考虑使用webwork APP.vue

<template>
  <div>
    <h2>this is app</h2>
    <div>
      <h2>this is Home component</h2>
      <Home></Home>
    </div>
    <div>
      <h2>this is Home component</h2>
      <About></About>
    </div>

    <button @click="handleAdd">增加</button>

    <p>{{ count }}</p>
    <p v-if="loading">loading....</p>
    <p v-if="loading">数据加载完成</p>
  </div>
</template>

<script>
import Home from "./pages/Home";
import About from "./pages/About";

export default {
  components: { Home, About },
  data() {
    return {
      count: 1,
      loading: true
    };
  },
  mounted() {
    const worker = new Worker(new URL("./deep-thought.js", import.meta.url));
    worker.postMessage({
      question:
        "The Answer to the Ultimate Question of Life, The Universe, and Everything."
    });
    worker.onmessage = (data) => {
      this.count = data.data.answer;
      this.loading = false;
    };
  },
  methods: {
    handleAdd() {
      this.count++;
      console.log(this.count);
    }
  }
};
</script>


deep-thought

import axios from "axios";
self.onmessage = ({ data: { question } }) => {
  axios.get("http://192.168.2.119:8081").then((res) => {
    let count = 0;
    for (let i = 0; i < 10000000000; i++) {
      count += i;
    }
    self.postMessage({
      answer: count,
      data: res.data
    });
  });
};