首页 > web前端 > js教程 > 在 Javascript 中利用多线程 - 不是关于 WebWorkers 也不是 WebAssembly

在 Javascript 中利用多线程 - 不是关于 WebWorkers 也不是 WebAssembly

Mary-Kate Olsen
发布: 2024-12-06 04:11:22
原创
264 人浏览过

Utilize Multi-Thread in Javascript - Not About WebWorkers nor WebAssembly

众所周知,JavaScript 在单线程上运行。这个概念是如此不可或缺,以至于我们经常用名称来引用它 - 主线程。那么,这是否意味着你的多核 CPU 在运行 JavaScript 时毫无用处?

不完全是。

JavaScript 可能有点厚颜无耻,发送有关其线程的混合消息。

但即使不深入研究 Web Workers 和 WebAssembly 等更复杂的功能,基本的 JavaScript 也可以利用多线程工作流程。

需要明确的是,本文重点介绍基于 Web 浏览器的 JavaScript。其他环境(例如 Node.js)可能会以不同的方式处理事情。

我们将探索 JavaScript 中多线程和类多线程操作的选项。现实是 - 您可以而且应该在基本的、纯原生 JavaScript 中使用多线程工作流程。

“解释!”

很高兴。

JavaScript 是一种单线程语言,但它由网络浏览器解释 - 而浏览器是多线程软件。

有多种方法可以模拟和利用多线程,您可能在没有意识到的情况下使用它们。

此外,JavaScript 的行为方式通常看起来是多线程的,但实际上并非如此。这听起来像是作弊,但它有效地提供了我们期望从多线程行为中获得的好处。

基于事件的编程感觉是多线程的

JavaScript 是一个基于事件的系统。当浏览器处理 JavaScript 文件时,它会运行其中的所有代码。完全有可能编写不停止运行的代码 - 尽管这通常是一个坏主意,因为它可能会导致浏览器崩溃。

我们通常做的就是设置事件监听器并等待事件发生。此时,JavaScript 完全空闲。如果不从主线程卸载一些工作,这种空闲等待是不可能的。

事件本身不是多线程的,但事件的等待和检查是由浏览器处理的,而不是由 JavaScript 处理。此进程从主线程卸载。

异步函数和多线程

像 setTimeout 和 setInterval 这样的函数已经陪伴我们很多年了。他们减轻了主线程的等待时间。

让我们简化一下它的工作原理。在较低级语言中,计时器和等待通常是通过不断检查“时间到了吗?”来实现的。想象一下 while() 循环检查经过的时间并在时间正确时继续执行。现代方法可能会使用 CPU 中断来避免阻塞 CPU 线程。

但是,当您在 JavaScript 中使用 setTimeout 或 setInterval 时,语言在等待期间完全空闲,这再次意味着等待以某种方式在主线程之外进行处理。

Fetch 和其他异步 API

如今,我们从浏览器获得越来越多的异步函数和 API。 fetch API 是一个众所周知的例子。即使是较新的 API,例如剪贴板 API,也是异步的。

这意味着您要求浏览器执行一个操作,它会释放 JavaScript 的主线程(或允许它处理其他内容 - 稍后会详细介绍)并在完成时通知您(通过处理以下代码)。

异步与多线程

异步并不一定意味着多线程——并不总是、不直接。简单地将“async”这个词放在函数前面对你来说没有任何作用。

JavaScript 如何处理异步行为有两个关键组件:

1. 回调队列和事件循环

将回调队列视为一行等待运行的函数。这些函数在主线程中一一执行。当异步操作完成时 - 无论是事件、超时还是已完成的获取 - 其回调都会放置在此队列中。

事件循环负责当调用堆栈为空时将回调从队列移动到调用堆栈。

2. 浏览器处理的函数

网络浏览器控制某些操作,并可能为它们启动新线程 - 这包括 fetch 或 setTimeout 等操作。 JavaScript 不需要管理这些线程。当异步操作完成后,浏览器将相应的回调放入回调队列中,JavaScript 从那里继续执行。

考虑这段代码:

console.log('Start');

setTimeout(() => {
  console.log('This is asynchronous');
});

console.log('End');
登录后复制

输出:

Start
End
This is asynchronous
登录后复制

发生的事情是这样的:

  • “开始”已记录。
  • setTimeout 调用时没有超时参数,因此默认为 0 毫秒。当前调用堆栈清空后,回调将发送到浏览器执行。
  • “结束”已记录。
  • 主线程现在空闲,事件循环检查回调队列。
  • setTimeout 的回调函数被移至调用堆栈。
  • 记录“这是异步的”。

注意 setTimeout 缺少超时参数。这是推迟低优先级代码执行的常见技术 - 首先处理所有其他内容,完成后,执行“较低优先级”的事情。

它的作用是立即将 setTimeout 内的函数放入回调队列中,并在主线程空闲时第一时间运行。

requestAnimationFrame 的特例

requestAnimationFrame 是一个浏览器 API,它告诉浏览器您想要在计算和更新视图之前执行一些代码(将其视为视频游戏中的每秒帧数)。

出于某种原因,Web 开发人员不会考虑重绘和 FPS,但这就是它的工作原理。

它本质上是异步的(尽管不是多线程)。

在引入 requestAnimationFrame 之前,使用不指定时间的 setTimeout 经常用于类似的目的,尽管它远没有那么有用(只是聊胜于无)。

此函数将您的代码排队到一个特殊的堆栈中。就在渲染新帧(重绘)之前,浏览器运行此排队的代码。它非常适合对 DOM、HTML 或 CSS 进行更改(只有那些,请,我禁止您在那里进行计算!)。

真正的多线程选项

虽然本文与它们无关,但值得一提的是,您可以通过利用 Web WorkersWebAssembly 实现真正的多线程操作。这些允许您在后台线程中运行脚本。它们无法直接操作 DOM,但对于处理繁重的任务(矩阵计算,有人吗?)来说它们非常宝贵。这种方法经常用于基于 HTML5 的游戏 - 更复杂的游戏,而不仅仅是井字游戏。

最后的话

JavaScript 在隔离时仍然是单线程的。但在现实世界中,它是一种在网络浏览器的多线程环境中运行的解释语言。如果您的口译员向您提供帮助,请接受并使用它。

这篇文章涵盖了很多内容——基础知识、底层解释等等。但这通常就是发展的方式——一切都是相互关联的。这就是为什么我喜欢看更广阔的前景。有时,专注细节并不能解决问题。


异步曾经给你带来过调试噩梦吗?
我当然有 - 几年前,获得有错误的堆栈并不是什么大事。

以上是在 Javascript 中利用多线程 - 不是关于 WebWorkers 也不是 WebAssembly的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板