您可能知道,Javascript 是单线程的。为了更好地说明,这意味着一个线程处理事件循环。对于旧版浏览器,整个浏览器在所有选项卡之间共享一个线程。现代浏览器通过使用每个站点实例的进程或每个选项卡的不同线程对此进行了改进。尽管专用线程提高了网页的响应速度,但它仍然让每个选项卡无法同时处理多个脚本运行。
您可能想知道,为什么我们需要同时运行多个脚本?
计算机速度非常快,每个选项卡都使用自己的线程,网页应该没有问题。大多数情况下都是如此,直到您的浏览器运行复杂的算法或呈现复杂的可视化效果。这些费力的过程最终会阻塞主线程,从而减慢 UI 事件和触发器的速度。好在 2009 年,Javascript 推出了一个热门的新玩具 Web Workers 来解决这个问题。
那么什么是 Web Worker?
Web Workers 是从 HTML 页面执行的 Javascript 脚本,该页面在远离主执行线程的后台线程上运行。数据通过消息在主线程和工作线程之间发送。由于这些 worker 在与主执行线程不同的线程上运行,因此您可以利用 web worker 从浏览器运行处理密集型任务,而无需创建阻塞实例。
酷吧?让我们建造一些。
生成 Web Worker 非常简单。首先,我们需要从两个文件开始:一个主文件和一个包含工作程序将运行的代码的文件。单独的文件是必需的,因为 Web 工作者代码需要在隔离的线程中运行。让我们将文件命名为 main.js 和 worker.js。要创建一个新的 Worker 对象,我们会将 worker 文件的路径传递给 Worker 构造函数,如下所示:
//main.js
var worker = new Worker('worker.js');
现在我们有了一个工人,让我们向它发送一条消息。为此,我们必须调用 worker 对象的 postMessage() 函数。
//main.js
var worker = new Worker('worker.js');
worker.postMessage('生日快乐');
在这一点上,我可能应该指出网络工作者的警告。主线程和工作线程之间发送的数据被复制而不是共享. 在考虑内存分配和数据传输速度时,我们应该牢记这一点。
现在为了让我们的工作人员听到“生日快乐”消息,我们必须在 worker.js 文件中为消息事件设置一个事件侦听器。
//worker.js
self.addEventListener('message', function(e) {
// 要运行的代码
}
一旦工作文件听到消息事件,它就会运行功能块中的代码。就像任何其他普通函数一样编写它,但不是返回变量,而是使用 postMessage() 函数将消息发送回主线程。我们可以将已发送消息中的数据与 e.data 一起使用。
//worker.js
self.addEventListener('message', function(e) {
var message = e.data + 'to myself!';
self.postMessage(message);
}
在上面的代码中,我们发送消息“祝我生日快乐!” 回到主执行线程。因此,我们还需要在主文件中有一个消息事件侦听器来接收数据并对其进行操作。这是最终的 main.js 文件:
//main.js
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
console.log(e.data);
}
worker.postMessage('生日快乐');
在此示例中,我们将 console.log '祝我生日快乐!'。一个有用的提示是,虽然 worker.addEventListener 和 worker.postMessage 的顺序无关紧要,但我们在发送消息之前创建监听消息的能力是合乎逻辑的。
最后我们想关闭工作线程,我们可以在工作文件中添加一个 self.close() 函数。这是最终的 worker.js:
//worker.js
self.addEventListener('message', function(e) {
var message = e.data + 'to myself!';
self.postMessage(message);
self.close();
}
让我们回顾一下刚刚发生的事情:
我们的应用程序在 main.js 中创建了一个网络工作者,它运行来自 worker.js 的代码
它向工作人员发送一条包含字符串“生日快乐”的消息
具有“消息”事件侦听器的工作人员接收到消息并运行其中的代码。
工人附加了“我自己!” 消息数据创建“祝我生日快乐!” 并将其作为消息中的数据发送回 main.js
Main.js,它也有一个消息事件监听器,而不是 console.log '祝我生日快乐!'。
简单!
以下代码使用 web worker 来拆分解决 n-queens 的处理过程:
//nQueen.html
<!DOCTYPE html>
<html>
<head>
<title>n-queens solver</title>
</head>
<body>
<script type="src/queenWorker.js"></script>
<script>
var num = parseInt(prompt("enter num"));
var all = (1 << num) - 1;
count = 0;
for (var i = 0; i < num; i++) {
var cols = 2 ** i;
var ld = 2 ** (i + 1);
var rd = 0;
if (i > 0) {
rd = 2 ** (i - 1);
}
var myWorker = new Worker('queenWorker.js');
myWorker.addEventListener('message', function(e) {
count += e.data;
console.log('worker count: ', e.data);
}, false);
myWorker.postMessage([ld, cols, rd, all]);
}
</script>
</body>
</html>
//queenWorker.js
self.addEventListener('message', function(e) {
let count = 0;
var findSolutions = function(ld, cols, rd, all) {
let poss = ~(ld | cols | rd) & all;
if (cols === all) {
count++;
}
while (poss) {
let negPoss = poss * -1;
let bit = poss & negPoss;
//let bit = poss & -poss;
poss = poss - bit;
findSolutions((ld | bit) << 1, cols | bit, (rd | bit) >> 1, all);
}
};
findSolutions(e.data[0], e.data[1], e.data[2], e.data[3]);
self.postMessage(count);
}, false);
随意玩弄它!测试 web workers 的注意事项,出于安全原因,Chrome 不允许您在本地部署 web workers。请改用 Firefox。