Web Workers 入门
大家好,今天我来了解一下 Web Worker。我的想法是实现一个简单的 Web Worker,它的作用是在主程序index.js和 Worker之间来回发送消息worker.js。我希望通过这个简单的示例,向大家展示我是如何探索这个我并不熟悉的领域的。在实现这个简单的 Web Worker 之后,我们还会探讨一些“实际” Web Worker 的应用场景。
我很多项目都是这样开始的,通常就是一个 HTML 文件、一个 JavaScript 文件和一个 CSS 文件。但这个项目,我们甚至连 CSS 文件都不需要。
我的根目录下有几个文件夹,个人文件放在一个名为“projects”的文件夹里。打开终端后,我经常运行一些命令。
为了简洁起见,将它们合并如下:
cd projects/tmp && mkdir webworkers && cd webworkers && touch index.html index.js && echo "console.log('hello');" > index.js
它会把我带到一个临时目录,我把很多零零碎碎的东西都放在里面,这些东西我还没用它们做成项目,随时可以删除。任何放在这个临时文件夹里的项目都不值得我费心。如果我开始重视某个项目,我会把它移到上一级目录。
这些命令会创建一个名为 webworkers 的文件夹,然后添加两个文件,index.html和index.js。该文件index.js以开头,console.log('hello');这样我就可以在第一次打开所有内容时验证所有连接是否都已建立。
我使用VSCode作为编辑器。VSCode 自带Emmet工具包,只需输入内容html:5,按 Tab 键,即可输出一个样板 HTML 文档。
在结束的 body 标签之前,我手动输入了:
<script src="index.js"></script>
现在我有一个非常基础的静态网站。
简易Web服务器
我使用一个 Python 模块在某个目录下运行一个静态 Web 服务器。如果你安装了 Python 2,请在终端中运行以下命令:
python -m SimpleHTTPServer 8000
如果您使用的是 Python 3,请运行:
python -m http.server 8000
如果你比较懒,而且经常需要这样做,你也可以在 .bash_profile 或 shell 在加载时执行的任何文件中添加别名。
alias pserve="python -m http.server 8000"
Web 服务器启动后,打开浏览器,导航到localhost:8000并打开控制台。如果一切操作正确,您应该会看到“hello”字样。
Web Workers!
好的,我们来读一下文档。看来我们需要从一个单独的文件中创建一个工作进程,并且这个工作进程将在后台线程中运行。很好,我们来创建这个文件。
touch worker.js
它有一个名为onmessage 的事件处理程序,所以我们现在可以把整个文件都做成这个处理程序。
worker.js:
onmessage = function(e) {
console.log(e);
};
我应该解释一下我在这里的操作。我们可以onmessage直接使用 `this`,因为在工作线程上下文中,`this`self和this`this` 都指向全局工作线程作用域。这种作用域划分方式与你在主线程中直接使用 `this`fetch或 ` this` 的方式非常相似console,即使它们作用于 `window` 对象,因为主线程的全局作用域是 `Window`。
如果你想写得更详细一些,可以这样写:
self.addEventListener("message", function(e) {
self.console.log(e);
});
使用 `this.setTimeout` 的优势addEventListener在于不会覆盖事件上已有的监听器。如果使用类似内联的 `this.setTimeout` onmessage,则一次只能有一个事件监听器,如果声明了新的事件onmessage处理程序,则会覆盖第一个监听器。而使用 `this.setTimeout`,addEventListener您可以添加任意数量的监听器,但需要注意在不再需要监听器时如何将其释放。这是一个需要注意的小权衡。
总体来说,保持一致性可能更好addEventListener。如果我使用 `.` addEventListener,那就意味着我应该把我的函数单独分离出来,以防以后需要移除监听器。我一边这样做一边自言自语:“过早优化是万恶之源,但养成好习惯并非如此。”
function handleMessage(e) {
console.log(e);
}
addEventListener("message", handleMessage);
好了,这就是我们的 worker.js 文件,让我们创建它并发送一条消息。
index.js:
const worker = new Worker("worker.js");
worker.postMessage("Hello");
保存并刷新页面,看看控制台中发生了什么。
> MessageEvent {isTrusted: true, data: "Hello", ...}
太好了!这个 MessageEvent 对象有很多属性。我们仔细看看data,因为我看到它里面有“Hello”,这意味着data这是我们发送的内容。
我们来尝试发布一个对象!HTML规范中规定attribute any data,它可以是字符串以外的其他类型。
index.js:
const worker = new Worker("worker.js");
worker.postMessage({ your: "face", is: "cool" });
将 worker.js 中记录的内容更改为仅记录data事件的属性:
function handleMessage(e) {
console.log(e.data);
}
addEventListener("message", handleMessage);
> {你的脸:"很酷"}
嗯,这正是我想要看到的。现在我们知道如何向工作进程发送数据了,那么如何接收数据并将其返回呢?
让我们回到之前的步骤index.js,让我们的工人接收一条消息:
const worker = new Worker("worker.js");
worker.postMessage({ your: "face", is: "cool" });
function handleMessage(e) {
console.log(e.data);
}
worker.addEventListener("message", handleMessage);
让我们的 worker.js 在收到消息时发送该消息。
function handleMessage(e) {
if (e.data.is === "cool") {
postMessage("yes");
} else {
postMessage("how dare you?");
}
}
addEventListener("message", handleMessage);
保存并刷新:
是的
出色的。
我们index.js再修改一次,以确保所有数据都能被评估:
const worker = new Worker("worker.js");
worker.postMessage({ your: "face", is: "not cool" });
function handleMessage(e) {
console.log(e.data);
}
worker.addEventListener("message", handleMessage);
保存,然后再次运行:
你竟敢这样?
这样,我们就可以在索引和工作节点之间来回发送包含数据的消息了。
整理
当您不再需要事件监听器时,可以将其移除,既可以在主线程的工作对象上移除,也可以在工作线程的全局作用域上移除。
主线:
worker.removeEventListener("message", handleMessage);
工作线程:
self.removeEventListener("message", handleMessage);
这是个值得关注的重要因素。你的代码很可能运行在比你那台2014款MacBook Pro更便宜的硬件上,而你之所以还留着它,仅仅是因为你不想用新款糟糕的键盘。
此外,完成工作后,您可能需要将其终止。没有必要让它继续运行。这可以在主线程或工作线程中完成。
在主线程中,它看起来像这样:
worker.terminate();
在工作线程中,它看起来像这样:
self.close();
浏览器检查
好的,最后一点。几乎所有浏览器都支持 Web Workers,但 Opera Mini 仍然存在,而且不知为何仍然有人在使用 IE6。你可以通过一个简单的检查来验证尝试运行你的代码的浏览器是否支持 Web Workers:
if (window.Worker) {
...
}
用例
想象力是关键。这能让你在浏览器中实现真正的多线程功能。这不是 JavaScript 的特性,JavaScript 是单线程的。这是 HTML 的特性。
如果把应用的状态管理放到后台工作线程里呢?这样就可以让所有耗时的状态计算在后台运行,而让主线程来处理所有 UI 操作。这岂不是很棒?
如果把所有数据获取操作都放在一个 worker 里呢?这样,在等待服务器响应时,主线程就无需阻塞任何操作。
如果你正在开发一款数据密集型应用,并且希望用户能够上传 CSV 文件,然后将数据解析到你的应用中,而无需将其发送到服务器,该怎么办?Web Worker 可以有效地将文件处理从主线程卸载下来。
结论
我认为这是我们能做的最简单的工作进程了。事实证明,在我们完成这里的所有工作之后,剩下的只是编写更多的 JavaScript 代码而已。
每个工作线程都有一个全局上下文,但无法访问 DOM、window 或 document。不过,你可以访问一些通常位于 window 上下文中的内容,这些内容也位于工作线程上下文中。
这里还有很多东西值得探索。我做了一个简单的例子,只是一个概念验证,证明“没错,这东西确实有效”。下一步要做的是构建一些更复杂的功能,比如解析 CSV 文件而无需将其发送到服务器。
我认为探索自己不了解的事物非常重要。就这次而言,我了解到 Web Workers 的设置和使用有多么简单。每次学习到这样的小知识,都会在你日后构建项目时,帮助你做出正确的决策。
希望你从这篇博文中学到了一些有用的东西。如果你在生产环境中使用 Web Workers,我很想了解你具体用它们做什么!
文章来源:https://dev.to/joshuagilless/getting-started-with-web-workers-3cjb