您的位置:首页 > Web前端 > HTML5

HTML5新特性之Web Workers

2016-03-08 13:09 806 查看
我们知道浏览器端JavaScript是以单线程的方式执行的,也就是说JavaScript和UI渲染占用同一个主线程,那就意味着,如果JavaScript进行高负载的数据处理,UI渲染就很有可能被阻断,从而造成用户体验的大打折扣。Web Workers作为HTML5新特性之一,为浏览器端JavaScript开创了一种新的运行模式,使之能够在另外的线程中创建新的运行环境,以便使JavaScript能够在后台做一些费时的处理。下面我们就来详细介绍一下Web Workers方面的知识。

Web Workers可以通过加载一个脚本文件,进而创建一个独立的工作线程,在主线程之外运行。工作线程的创建比较简单,代码如下:

var worker = new Worker('js/worker.js');
如代码所示,我们只需将含有工作线程的代码文件路径作为参数传入到Worker构造函数中,即可创建一个工作线程,使其能够在后台执行。需要注意的是,脚本的加载有同源策略的限制,所以必须指定与主线程同源的文件。

在创建完工作线程后,我们就可以在主线程中与工作线程通信了。主线程中通过postMessage方法向工作线程发送消息,也可以通过捕获worker实例的message事件来接收来自工作线程的消息。需要注意的是,在postMessage方法中,我们可以相对自由的传递各种类型的数据,但是像document等BOM对象是无法被传递的。下面就让我们来看看主线程中是如何和工作线程交互的:

//message sending
worker.postMessage({name: 'Scott'});

//message receiving
worker.onmessage = function(event) {
var data = event.data;
//to do
}
同样的,在工作线程中我们也可以通过postMessage和onmessage方法来发送和接收数据,下面是工作线程文件worker.js中的代码:

//message sending
self.postMessage({name: 'Worker'});

//message receiving
self.onmessage = function(event) {
var data = event.data;
//to do
}

在某些情况下,我们不得不中断工作线程的运行,主线程中我们可以调用worker实例的terminate方法,在工作线程中我们也可以调用close方法中断自身,代码如下:

//interrupt a work thread in main thread
worker.terminate();

//interrupt work thread it self
self.close();
以上介绍了Web Workers的基本用法,接下来我们来以一个实例说明它在实际项目中是如何应用的。

我们经常会加入一些技术群组里探讨技术,广交朋友,如果群组成员太多,查看起来就不太方便,为此我们打算给这个群组加一个搜索的功能,根据关键字搜索与成员信息匹配的数据,就像下面图中所示:



在这个案例中,群组成员信息预先加载到了客户端,搜索功能也是在客户端完成的,假如数据非常多,一个简单的搜索可能就会在用户体验上造成不小的折扣,我们打算用Web Workers来实现这个操作。现在来看一下示例项目的基本目录结构:



我们可以看到,在js目录中有个worker.js文件,用于放置工作线程的逻辑代码,此文件在运行时被主线程加载,我们稍后会详细讲解。现在我们先看一下主页面的基本结构:

<html>
<head>
<title>Web Worker</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
</head>
<body>
<div id="searching">
<input id="keywords" type="text" placeholder="type in to start searching">
<button id="search-button" onclick="search();">search</button>
<div id="members">
<!-- <div class="member">
<img src="img/icon.png">
<div class="info">
<p>John Li</p>
<p>Java programming, Python language</p>
</div>
<a class="add">+Add</a>
</div> -->
<!-- the search results will be here -->
</div>
</div>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
看起来很简单,一个搜索框和搜索按钮,下面是搜索结果显示区域。为了使其运行起来,我们还需要main.js的配合,代码如下:

//a simple group member data example
var groupMembers = [
{
id: 101,
name: 'John Li',
skills: 'Java programming, Python language, MySQL'
},
{
id: 102,
name: 'Lisa Wang',
skills: 'HTML, JavaScript, CSS, Node.js'
},
{
id: 103,
name: 'Tom Wang',
skills: 'JavaScript, CSS, HTML5'
},
{
id: 104,
name: 'Andy Zhang',
skills: 'PHP language, JavaScript programming, CSS'
}
/*
a large number of data
*/
];

//loading the worker.js to instantiate a Worker object
var worker = new Worker('js/worker.js');

//receive message from work thread, and then render the result data
worker.onmessage = function(e) {
renderGroupMembers(e.data.results);
};

function search() {
var keywords = $('#keywords').val().trim();

//post a message to work thread
worker.postMessage({
groupMembers: groupMembers,
keywords: keywords
});
}

function renderGroupMembers(members) {
var html = '';
members.forEach(function(member) {
var resultHtml = '<div class="member">'
+ '	<img class="icon" src="img/icon.png">'
+ '	<div class="info">'
+ '		<p>' + member.name + '</p>'
+ '		<p>' + member.skills + '</p>'
+ '	</div>'
+ '	<a class="add">+Add</a>'
+ '</div>';
html += resultHtml;
});

$('#members').html(html);
}

renderGroupMembers(groupMembers);
在这段代码中,我们首先会加载worker.js,实例化一个Worker对象,然后捕获message事件,进而将搜索结果显示到相应区域。而search函数负责监听搜索按钮的点击事件,然后向工作线程发送带有群组数据和关键字的消息,将搜索任务交给工作线程。

最后,我们开看看worker.js是如何运行的:

//receive the message from main thread
self.onmessage = function(e) {

var groupMembers = e.data.groupMembers;
var keywords = e.data.keywords;

var results = searchByKeywords(groupMembers, keywords);

//post the result message to main thread
self.postMessage({results: results});
};

//it may be quite complicated in real application
function searchByKeywords(groupMembers, keywords) {
var results = [];

keywords = keywords.toLowerCase();

groupMembers.forEach(function(member) {
var nameMatched = member.name.toLowerCase().indexOf(keywords) > -1;
var skillsMatched = member.skills.toLowerCase().indexOf(keywords) > -1;

if (nameMatched || skillsMatched) {
results.push(member);
}
});

return results;
}
从上面的代码中可以看到,首先我们会捕获工作线程的message事件,接收群组数据和关键字,然后调用搜索函数,最后将搜索结果发送回主线程,完成这次的搜索任务。

以上就是Web Workers的介绍,在实际开发中,可能面临很多复杂的操作需要在前端完成,我们都可以考虑用Web Workers实现,将复杂操作交给工作线程,进而减少主线程的阻塞,在用户体验上一定会有显著的提升。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: