首先声明,标题写成多线程主要目的是吸引眼球赚点击量,程序员一般看见多线程就会肾上腺素激升,你懂的。。。
其实更精确的定义是工作线程worker thread,本篇会详细为何起名叫工作线程worker thread,而非其他语言中的多线程multi thread,两者有些许差别。
JavaScript原本是单线程的,一次只能处理一件事。如果处理数组占据大量时间,用户点击可能就没反应了,或页面刷新很慢,影响用户体验。
HTML5之后可以新建一个工作线程来处理数组,确保之后的事情能顺利完成。
工作线程大致定义如下:
由一个单独的JavaScript文件定义。要让工作线程工作,浏览器会发送一个消息,工作线程收到消息开始工作。完成工作后发回消息给浏览器。
1.HTML中加载的js相当于主线程:
<script src="js/myThread.js"></script> //可以将HTML中加载的myThread.js理解为主线程
2.定义个worker.js,内容可以暂时为空。(为定义工作线程做准备)
3.主线程中定义工作线程,发送消息让工作线程开始工作,定义onmessage和onerror回调函数,分别处理工作线程成功或失败完成工作的事件
window.onload = function() { //HTML页面加载时运行代码 var worker = new Worker("js/worker.js"); //将第2步新建的空的<span style="color:black;"><span style="color:black;">worker.js</span></span>定义成工作线程(※1) worker.postMessage("startwork"); //主线程向工作线程发送消息(※2) worker.onmessage = function(event) { //工作线程成功完成工作后的回调函数(※3) document.getElementById("output").innerHTML = event.data; } worker.onerror = function(error) { //工作线程完成工作失败后的回调函数(※3) document.getElementById("output").innerHTML = "There was an error in " + error.filename + " at line number " + error.lineno + ": " + error.message; }; }
※1:只能用一个js文件创建工作线程,而不能用函数。因为规定工作线程不能访问DOM,如果向Worker构造函数传入一个函数,该函数可能包含DOM或主JavaScript代码的引用,就违反规则。因此工作线程的设计者选择的做法是只能传递一个js文件的URL
※2:消息可以是一个简单的字符串,也可以更复杂如worker.postMessage([1,2,3,4]);数组,worker.postMessage({“message”:“ping”,
“count”: 5});JSON对象。但不能worker.postMessage(func);发送函数,理由见※1。
※3:回调函数的参数Event对象:我们只对data和target属性感兴趣,data属性包含工作线程发送的消息,target属性是发出这个消息的工作线程的引用,方便我们知道来自哪个工作线程
4.实现worker.js
onmessage = function(event) { if (event.data == "startwork") { //根据主线程发来的消息,做相应的处理 ...... //此处省略50行业务相关的处理工作 postMessage("done!"); //工作完成,回发消息通知主线程,主线程的onmessage方法将被触发 } else { postMessage("Are you sleepwalking now?"); } }
工作线程注意点:
1.上面的※1已经说明过了,这里再啰嗦一遍:
工作线程无法访问浏览器代码能够访问的很多运行时的对象,如DOM或主代码中的所有变量和函数。但可以访问localStorage或做出XMLHttpRequest请求。
之所以如此设计,是因为必须保证只有一个线程能访问DOM,否则多个线程并发修改DOM会很容易导致DOM处于一种不一致的状态。
想象一下线程A为某个DOM元素增加子DOM元素,线程B删除该DOM元素。而线程之间无法保证运行顺序,如果线程B先完成,线程A取DOM元素时将会出错。
现在明白工作线程和传统意义上的多线程的区别了吧,工作线程相当于一个二等公民。
2.主线程向工作线程发送的消息对象,不会成两者间的共享对象。工作线程会得到对象的副本,工作线程中对副本的修改不会影响到主线程中该对象,工作线程发出的对象也是如此,主线程只能得到该对象的副本
何时使用工作线程:
学习了工作线程的基本原理和技术后,请不要滥用它。因为工作线程的负荷是很重的,通常数据量不大时压根没必要使用工作线程。
但在页面做图像处理时就非常有用了,不用工作线程的话,加载显示Mandelbrot分形图页面的速度将难以忍受: