js是单线程的,如何实现多线程?一份WebWorker食用指南附上

一个切图仔的自我救赎
我的开源工具库:mdollar
之后准备学习:unocss、浏览器的垃圾回收机制(v8)

前言

“js是单线程的,如何实现多线程?”当这句话从面试官口中说出来的时候,怎么去回答,才疏学浅的我,好奇心就一下被勾起来了。甚至脑中已经yy到了我与面试官对峙的场景:

面试官:“js是单线程的,如何实现多线程?”

我:“咱就是说************ ,你 ************吧”(此处省略一万字…)

以上开玩笑,接下来进入正题

作为前端的我们多多少少有听到过关于浏览器和js的线程以及进程问题,有时候可能会傻傻的分不清楚,今天就一起把线程进程都好好学习一下,一起看看对500W的数据排序并且不影响主线程的实现原理。

线程和进程的区别

“进程是操作系统分配资源的单位,线程是调度的基本单位,线程之间共享进程资源”

简单来说:“一个进程下可以包含多个线程,这些线程共享这个进程的资源。”

来个更直观的栗子:任务管理器

在这里插入图片描述
在这里插入图片描述

单线程和多线程

打个比方:现在的你刚搬完家,有很多东西要收拾,你一个人收拾完,那就是单线程。你喊上你的对(da)象(ge),两个人一起收拾,那就是多线程,如果你喊一车面包人来一起收拾,没错,也是多线程。这个比喻应该很容易理解了。

为什么要学多线程

现在有一个需求,有500W条数据给你处理,js是单线程的,那么处理大量的数据就会导致ui卡顿,给用户的体验非常的不好,那这时候老板跟你说喊你优化一下:

老板:“小b啊,你小子过来把这个处理一下,怎么这么卡?”

我:“这你电脑卡(手机卡)跟我有啥关系?不知道js是单线程的么?”

老板:“哦,原来是这样,那为什么别人家的网站不卡”

我:“你************”

这时候就得上多线程,也就是今天的主角WebWorker,虽然说像上面这种需求基本很少见,但是我可以不用,但我不能不会啊

WebWorker介绍

Web Worker 是 HTML5 标准的一部分,这一规范定义了一套 API,允许我们在 js 主线程之外开辟新的 Worker 线程,并将一段 js 脚本运行其中,它赋予了开发者利用 js 操作多线程的能力。

因为是独立的线程,Worker 线程与 js 主线程能够同时运行,互不阻塞。所以,在我们有大量运算任务时,可以把运算任务交给 Worker 线程去处理,当 Worker 线程计算完成,再把结果返回给 js 主线程。这样,js 主线程只用专注处理业务逻辑,不用耗费过多时间去处理大量复杂计算,从而减少了阻塞时间,也提高了运行效率,页面流畅度和用户体验自然而然也提高了

WebWorker的简单使用

举个非常简单的栗子

index.html

 let wk = new Worker("workerMain.js");
// 接收worker发来的消息
wk.onmessage = (evnet) => {
    
    
  console.log("收到worker发来的消息,", evnet.data);
};
// worker出错后抛出的钩子函数
wk.onerror = (err) => {
    
    
  stopWorker();
  console.log(error.filename, error.lineno, error.message); // 发生错误的文件名、行号、错误内容
};
// 发送消息给worker
function sendMessageToWorker() {
    
    
  wk.postMessage("发工资了!!!!");
}
// 关闭worker
function stopWorker() {
    
    
  wk.terminate();
}

workerMain.js

let i = 1
//  给主线程发送消息
function sendMessageToMainThread() {
    
    
    i++
    self.postMessage(i)
    setTimeout(simpleCount, 1000)
}
sendMessageToMainThread()
// 接收主线程发的消息
self.onmessage = (event) => {
    
    
    console.log(`老板你在说啥?哦,原来你跟我说:${
      
      event.data}`);
}

vue中使用

如果想在vue中使用这玩应,还不能直接用,需要安装worker-loader,来解析web worker,

我们直接上vueuse的库,用hooks中的useWebWorkerFn来实现这个功能,就能实现大部分场景的需求
这是一个官网的预览效果,展示了在主线中排序500万条数据和在子线程中排序的简单示例

在这里插入图片描述

//安装
npm i @vueuse/core
// 栗子
<script setup lang="ts">
import {
    
     computed, nextTick, ref } from "vue";
import {
    
     useDateFormat, useTimestamp, useWebWorkerFn } from "@vueuse/core";
/** 排序方法 */
const heavyTask = () => {
    
    
	const randomNumber = () => Math.trunc(Math.random() * 5_000_00);
	const numbers: number[] = Array(5_000_000).fill(undefined).map(randomNumber);
	numbers.sort();
	return numbers.slice(0, 5);
};

const {
    
     workerFn, workerStatus, workerTerminate } = useWebWorkerFn(heavyTask);
/**
 * 排序方法运行状态
 */
const running = computed(() => workerStatus.value === "RUNNING");

// 使用时间戳函数,这个time会持续更新
const time = useTimestamp();
// 格式化时间戳
const computedTime = useDateFormat(time, "YYYY-MM-DD HH:mm:ss SSS");
/** 数据 */
const data = ref<number[] | null>(null);
/** 当前执行的类型(主线程还是多线程) */
const runner = ref("");

/**
 * 主线程中排序
 */
const baseSort = async () => {
    
    
	data.value = null;
	await nextTick();
	data.value = heavyTask();
	runner.value = "Main";
};
/**
 * 多线程排序
 */
const workerSort = async () => {
    
    
	data.value = null;
	await nextTick();
	data.value = await workerFn();
	runner.value = "Worker";
};
</script>

<template>
	<p>
		当前时间: <b>{
    
    {
    
     computedTime }}</b>
	</p>
	<note class="mb-2"> 这是一个Demo </note>
	<button @click="baseSort">在主线程中排序</button>
	<button v-if="!running" @click="workerSort">在Worker中排序</button>
	<!-- 结束线程正在执行的方法 -->
	<button v-else class="orange" @click="workerTerminate('PENDING')">结束worker执行</button>
	<p v-if="data">
		执行线程: <strong>{
    
    {
    
     runner }}</strong
		><br />
		执行结果: <strong>{
    
    {
    
     data }}</strong>
	</p>
</template>

猜你喜欢

转载自blog.csdn.net/wz9608/article/details/129585039