浏览器的多线程和单线程
JavaScript 的宿主浏览器只有一个线程运行 JavaScript ,除了 JavaScript 的线程,浏览器中单个页面还有一些其他线程,例如:
- UI 线程 负责处理渲染 DOM 元素;
- GUI 线程 用于处理与用户交互的逻辑;
- 网络线程 用于发送接收 HTTP 请求;
- file 线程 用于读取文件;
- 定时器线程 处理定时任务等等;
Web Worker 特点
优点
通过加载一个 JS 文件来进行大量复杂的计算,而不挂起主进程;通过 postMessage 和 onMessage 进行通信;
可以在 Worker 中通过 importScripts(url) 方法来加载 JavaScript 脚本文件;
可以使用 setTimeout()、clearTimeout()、setInterval() 和 clearInterval() 等方法;
可以使用 XMLHttpRequest 进行异步请求;
可以访问 navigator 的部分属性;
可以使用 JavaScript 核心对象;
局限性
不能跨域加载 JavaScript;
Worker 内代码不能访问 DOM;
使用 Web Worker 加载数据没有 JSONP 和 Ajax 加载数据高效;
使用介绍
创建子线程
// 判断浏览器是否支持 web worker
if (window.Worker) {
var worker = new Worker('test.js'); // 创建一个线程,参数为需要执行的 JavaScript 文件
}
向线程传递参数
新的线程的上下文环境跟原宿主环境相对独立的,所以变量作用域不同,如果需要互相读取变量的话需要通过消息发送的方式传输变量
// 数据类型可以是字符串 'test'
worker.postMessage('test');
// 数据类型可以是对象 { method: 'echo', args: ['Work'] }
worker.postMessage({ method: 'echo', args: ['Work'] });
主线程接受消息
主线程也需要通过监听的方式获取辅线程的消息
worker.onmessage = function (event) {
console.log('接收到消息: ' + event.data);
}
线程加载脚本
子线程内部也可以通过函数加载其他脚本
importScripts('script1.js', 'script2.js');
关闭线程
// 主线程中关闭子线程
worker.terminate();
// 子线程关闭自身
// Worker 线程中全局对象为 self,代表子线程自身,这时 this 指向 self
self.close();
错误处理
主线程可以监听 Worker 是否发生错误,如果发生错误,Worker 会触发主线程的 error 事件;
Worker 内部也可以监听 error 事件;
worker.onerror(function (event) {
console.log([
'ERROR: Line ', event.lineno, ' in ', event.filename, ': ', event.message
].join(''));
});
// 或者
worker.addEventListener('error', function (event) {
// ...
});
// 发送的数据无法序列化成字符串时,会触发 messageerror 事件
Worker.onmessageerror(function (event) {
// todo
});
适用场景
并行计算;
ajax 轮询;
耗时的函数执行;
数据预处理 / 加载;
案例:多线程实现非阻塞全排列
-
什么是全排列:从 n 个不同元素中任取 m (m ≤ n) 个元素,按照一定的顺序排列起来,叫做从 n 个不同元素中取出 m 个元素的一个排列;当 m=n 时所有的排列情况叫全排列;
-
HTMLJavaScriptJavaScriptJavaScript
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JavaScript实现全排列</title> </head> <body> <input type="text" id="str" /> <button onclick="combine()">全排列</button> 结果是:<div id="result" style="width:500px;height:500px;word-break: break-all;"></div> </body> <script type="text/JavaScript"> // 点击按钮向 webworker 线程发送请求 function combine() { var worker = new Worker('http://127.0.0.1:5500/worker.js'); worker.postMessage(document.getElementById("str").value); worker.onmessage= function (event) { document.getElementById("result").innerHTML = event.data ; // 监听 JavaScript 线程的结果 }; } </script> </html>
// worker.js importScripts('script1.js', 'script2.js'); // 监听主线程的数据请求 self.onmessage = function (message) { var msg = message.data; if (msg == "") { postMessage("请输入正确的字符串"); } else { postMessage(getGroup(msg.split(""))); } }
// script1.js // 生成全排列 function getGroup(data, index = 0, group = []) { var need_apply = new Array(); need_apply.push(data[index]); for (var i = 0; i < group.length; i++) { need_apply.push(group[i] + data[index]); } group.push.apply(group, need_apply); if (index + 1 >= data.length) { return group; } else { return getGroup(data, index + 1, group); } }
// script2.js console.log('script2.js');
API 篇:WebSocket
上一篇