HTML5的一个重要特性是支持多线程(Web Worker),这使得在浏览器执行JavaScript代码时可以使用多个线程加快程序运行速度,提升用户体验。
前置知识
在介绍Web Worker之前,需要先了解下JavaScript中的单线程和异步编程。JavaScript运行在浏览器端时只有一个主线程,在这个主线程中执行各种操作,包括用户交互和执行代码等等,因此当运行大量计算密集型操作或IO操作时,主线程可能会被阻塞,使得程序变得很慢。
为了解决这个问题,异步编程思想被引入JavaScript中,通过回调函数等方式实现异步操作,使程序能在等待IO操作时依然能够继续执行其他操作。
但是,JavaScript中的异步编程虽然能够缓解单线程带来的问题,但是仍然不能解决在计算密集型任务中单线程的阻塞问题。Web Worker就是为了解决这个问题而被设计出来的。
Web Worker的概念和使用
Web Worker是一种运行在浏览器后台的脚本,可以在主线程之外创建线程来执行JavaScript代码,不会影响主线程的运行和渲染。所以,在使用Web Worker时需要注意,worker中不能操作DOM,也不能访问主线程中的变量和函数等内容。
使用Web Worker非常简单。首先,我们需要通过new Worker()
函数来创建一个worker对象,传入worker的脚本文件路径。例如:
var worker = new Worker('worker.js');
在此之后,我们就可以向worker对象发送消息,以让它执行对应的逻辑。例如:
worker.postMessage('hello');
在worker的脚本文件中,我们需要用onmessage来监听从主线程传过来的消息,并根据消息内容执行相应的逻辑。例如:
onmessage = function(event) {
console.log('from main thread: ' + event.data);
postMessage('hello back');
}
在上述代码中,我们定义onmessage事件处理函数来接收主线程传来的消息,并打印到console中,然后再通过postMessage函数将一个”hello back”的消息发送回主线程。
最后,我们需要在主线程中通过监听message事件,来处理接收到的来自worker的消息:
worker.onmessage = function(event) {
console.log('from worker: ' + event.data);
}
这样我们就完成了worker的创建、消息的发送和接收。
Web Worker的示例
接下来,我们通过两个示例来演示Web Worker的使用。
示例1:计算10000000以内质数的个数
我们为了演示多线程在计算密集型操作中的优势,我们来计算10000000以内质数的个数。首先,我们来看看单线程运行这个任务的效率:
function isPrime(n) {
if (n <= 1) {
return false;
}
for (var i = 2; i < n; i++) {
if (n % i === 0) {
return false;
}
}
return true;
}
function countPrimes(start, end) {
var count = 0;
for (var i = start; i <= end; i++) {
if (isPrime(i)) {
count++;
}
}
return count;
}
var start = new Date().getTime();
console.log('Total primes: ' + countPrimes(2, 10000000));
var end = new Date().getTime();
console.log('Elapsed time: ' + (end - start)/1000 + 's');
这段代码会输出在10000000以内的质数个数和程序运行的时间,由于我们是在单线程中进行运算,因此运算时间可能较长。
我们可以通过Web Worker来解决运算时间长的问题。首先我们来创建一个worker:
var worker = new Worker('worker.js');
在worker.js文件中,我们需要实现countPrimes函数,代码如下:
function isPrime(n) {
if (n <= 1) {
return false;
}
for (var i = 2; i < n; i++) {
if (n % i === 0) {
return false;
}
}
return true;
}
onmessage = function(event) {
var data = event.data;
var count = 0;
for (var i = data.start; i <= data.end; i++) {
if (isPrime(i)) {
count++;
}
}
postMessage(count);
}
我们定义onmessage事件处理函数来接收主线程传来的消息,并根据传过来的start和end范围计算其中的质数个数,最后将结果通过postMessage函数发送给主线程。
在主线程中,我们首先定义一个promise对象,用来监听工作线程发送过来的结果。当结果返回后,promise.then方法会执行回调函数来输出结果和运行时间:
var promise = new Promise(function(resolve, reject) {
worker.onmessage = function(event) {
resolve(event.data);
}
});
promise.then(function(result) {
var end = new Date().getTime();
console.log('Total primes: ' + result);
console.log('Elapsed time: ' + (end - start)/1000 + 's');
});
var start = new Date().getTime();
worker.postMessage({start: 2, end: 10000000});
多线程的代码和单线程中的代码对比,我们发现多线程的代码比单线程的代码更加复杂,但通过使用多线程,我们可以在执行计算密集型任务时提高运行效率。
示例2:通过Web Worker读取本地文件
在这个示例中,我们通过Web Worker来读取本地文件,我们在Web Worker中使用了FileReader读取文件并提取文件内容。主线程仅仅通过Web Worker向工作线程发送消息。
在主线程中,我们首先实例化一个worker对象:
var worker = new Worker('worker.js');
在Web Worker中,我们可以使用FileReader的api读取文件内容,代码如下:
onmessage = function(event) {
var file = event.data;
var reader = new FileReader();
reader.onload = function(event) {
var data = event.target.result;
postMessage(data);
};
reader.readAsText(file);
};
在主线程中,我们首先定义一个input元素和一个button元素,用户可以使用这些元素来选择文件。在选择文件后,我们通过Web Worker来读取此文件的内容:
var input = document.createElement('input');
input.type = 'file';
document.body.appendChild(input);
var button = document.createElement('button');
button.textContent = 'Read File';
document.body.appendChild(button);
button.onclick = function() {
var file = input.files[0];
var promise = new Promise(function(resolve, reject) {
worker.onmessage = function(event) {
resolve(event.data);
};
});
promise.then(function(result) {
console.log(result);
});
worker.postMessage(file);
};
通过以上的代码,我们可以通过Web Worker读取本地文件,节约了主线程所需的时间和计算功耗。
总结
- Web Worker是一个能够让JavaScript中使用多线程的API。
- Web Worker能够在浏览器中创建线程,协同工作线程和主线程,同时提高程序运算效率并降低程序响应时间。
- 在Web Worker中,不支持DOM和主线程中的变量和函数等内容,因此Web Worker使用场景比较有限。
- 通过两个实例的演示,可以发现Web Worker在计算密集型任务执行中具有很好的优势,可以大大提升程序的运行效率。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:HTML5之多线程(Web Worker) - Python技术站