第八章 Web Workers API - HTML5 程序设计

返回介绍

第八章 Web Workers API

发布于 2017-06-10 字数 26805 浏览 1070 评论 0

HTML5 Web workers可以让Web应用程序具备后台处理能力。它对多线程的支持性非常好,因此,使用了HTML5的JavaScript应用程序可以充分利用多核CPU带来的优势。

将耗时长的任务分配给自HTML5 Web Workers执行,可以避免弹出脚本运行缓慢的警告,见图8-1。图中显示的是JavaScript程序循坏持续几秒钟后弹出的警告窗口。

图8-1 Firefox中的脚本运行缓慢警告

图8-1 Firefox中的脚本运行缓慢警告

尽管Web workers功能强大,但也不是万能的,有些事情它还做不到。例如,在WebWorkers中执行的脚本不能访问该页面的window对象(window.document),换句话说,Web workers不能直接访问Web页面和DOM API。 虽然Web workers不会导教阔览器UI停止响应,但是仍然会消耗CPU周期,导致系统反应速度变慢。

如果开发人员创建的Web应用程序需要执行一些后台数据处理,但又不希望这些数据处理任务影响Web页面本身的交互性,那么可以通过WebWorkers生成一个Web workers去执行数据处理任务,同时添加一个事件监听器去监听它发出的消息。

Web Workers的另一个用途是监听由后台服务器广播的新闻信息,收到后台服务器的消息后,将其显示在Web页面上。像这种与后台服务器对话的场景中,Web Workers可能会使用到WebSockets或Server-sent事件。

在本章中,我们将探讨如何使用WebSockets。首先,讨论WebSockets的工作机制和在编写本书时各浏览器的支持情况。接下来,将探讨如何使用API创建新的worker以及如何在worker和生成该worker的上下文之间进行通信。最后,我们会演示如何建立一个Web Workers应用。

8.1 HTML5 Web Workers

浏览器对于Web workers的支持情况各不相同,并且扔在持续更新发展中。如表8-1所示,在编写书本时,Web Workers已经得到了很多浏览器的支持。

表8-1 浏览器支持情况

浏览器 支持情况
Chrome 版本3及以上支持
Firefox 版本3.5及以上支持
Internet explorer 暂不支持
Opera 版本10.6及以上支持
Safari 版本4及以上支持

8.2 使用HTML5 Web Workers API

本节,我们将从细节上探讨Web Workers API的使用。为便于说明。我们创建了一个简单的页面:echo.html。Web Workers的使用方格非常简单,只需创建一个Web Workers 对象,并传入希望执行的JavaScript文件即可。另外,在页面中再设置一个事件监听器,用来监听由WebWorker发来的消息和错误信息。如果想要在页面与WebWorkers之间建立通信,数据需通过postmessage函数传递。对于Web Workers JavaScript文件中的代码也是如此:必须通过设置事件处理程序来处理发来的消息和错误倍息,通过调用postMessage函数实现与页面的数据交互。

8.2.1 浏览器支持性检查

在调用Web WorkersAPI函数之前,首先要确认浏览器是否支持。如果不支持。可以提供一些备用信息,提醒用户使用最新的浏览器。代码清单8-1是可以用来测试浏览器支持性的代码。

代码清单8-1 检查浏览器支持情况的代码

function loadDemo() {
    if (typeof(Worker) !== "undefined") {
        document.getElementById("support").innerHTML = "Excellent! Your browser supports HTML5 Web Workers";
    }
}

这个示倒中,使用loadDemo函数来检测浏览器的支持情况,可在页面加载时调用该函数。调用typeof(Worker)会返回全局Window对象的Worker属性,如果浏览器不支持Web Workers API,返回结果将是“undefined”。这段代码在检测了浏览器支持性之后,会将检测结果反映到页面上。通过重新页画中预先定义好的support元素中的内容,为用户显示适当的提示, 如图8-2所示。

图8-2 检测HTML5 Web Workers支持性的示例

图8-2 检测HTML5 Web Workers支持性的示例

8.2.2 创建HTML5 Web Workers

Web Workers初始化时会接受一个JavaScript文件的URL地址,其中包含了供Worker执行的代码。这段代码会设置事件监听器,并与生成Worker的容器进行通信。JavaScript文件的URL可以是相对或者绝对路径,只要是同源(相同的协议、主机和端口)即可:

worker = new Worker("echoWorker.js");

8.2.3 多个JavaScript文件的加载与执行

对于由多个JavaScript文件组成的应用程序来说,可以通过包含<script>元素的方式。在页面加载的时候同步加载JavaScript文件。然而,由于WebWorkers没有访问document 对象的权限,所以在Worker中必须使用另外一种方法导人其他的JavaScript文件—importScripts:

importScripts("helper.js");

导人的JavaScript文件只会在某一个已有的Worker中加载和执行。多个脚本的导入同样也可以使用importScripts函数,它们会按顺序执行:

importScripts("helper.js", "anotherHelper.js");

8.2.4 与HTML5 Web Workers通信

Web worker一旦生成。就可以使用postMessage API传送和接收数据。postMessage API还支持跨框想和跨窗口通信。大多数JavaScript对象都可以通过postMessage发迭,但含有循环引用的除外。

假设要建立这样一个简单的Web Workers示倒:用户可以向Worker发送信息,然后Worker把信息返回来。也许这个示例并没有太大的实际意义,但是其理念是构建复杂应用时所必需的。图8-3显示的是这个示例的Web 页面和正在执行中的Web Workers。对应的程序代码列子本节的末尾。

图8-3 一个简单的使用HTML5 Web Workers的Web页面

图8-3 一个简单的使用HTML5 Web Workers的Web页面(本示例需在安装了Firebug的Firefox浏览器中测试,并请启用firebug控制台。)

为了能与Web Workers成功通信,除了要在主页(调用WebWorkers的页面)中添加代码以外,Worker JavaScript文件中也需要添加相应代码。

8.3 编写主页

为实现页面到Web Workers 的通信,我们将调用postMessage函数以传入所需数据。同时,我们将建立一个监听器。用来监听由WebWorkers发送到页面的消息和错误信息。

为建立主页和Web Workers之间的通信,首先在主页中添加对postMessage函数的调用,如下所示:

document.getElementById("helloButton").onclick = function() {
    worker.postMessage("Here's a message for you");
}

用户点击”post a Message”按钮后,相应信息会被发送给Web Workers。然后。我们将事件监听器添加到页面中,用来监听从WebWorkers发来的信息:

worker.addEventListener("message", messageHandler, true);
function messageHandler(e) {
    // process message from worker
}

编写HTML5 Web Workers JavaScript文件

在Web Workers JavaScript文件中,也需要添加类似的代码:必须添加事件监听器以监听发来的消息和错误信息,并且通过调用postMessage函数实现与页面之间的通信。

为了完成页面与WebWorkers 之间的通信功能,首先,我们添加代码调用postMessage 函数。例如,在messageHandler函数中可以添加如下代码:

function messageHandler(e) {
    postMessage("worker says: " + e.data + " too");
}

接下来,在Web Workers JavaScript文件中添加事件监听器,以处理从主页发来的信息:

addEventListener("message", messageHandler, true);

在这个示例中,接收到信息后会马上调用messagehandler函数以保证信息能及时返回。

8.3.1 处理错误

HTML5 Web Workers脚本中未处理的错误会引发Web Workers对象的错误事件。特性是在调用到了Web Workers的脚本时,对错误事件的监听就显得尤为重要。下面显示的是Web Workers JavaScript文件中的错误处理函数,他将错误记录在控制台上:

function errorHandler(e) {
    console.log(e.message, e);
}

为了处理错误,还必须在主页上添加一个事件监听器:

worker.addEventListener("error", errorHandler, true);

8.3.2 HTML5 Web Workers

Web Workers不能自行终止。但能够被启用它们的页面所终止。开发人员都希望在不再需要Web Workers时回收其所占资源,比如当Web Workers通知主页它已执行完成的时候。另外,还有可能在用户干预的情况下取消一个运行耗时较长的任务,等等。这些情况下我们都需要终止Web Workers,可以调用terminate函数来实现。被终止的WebWorkers 将不再响应任何信息或者执行任何其他的计算。终止之后, Worker不能被重新启动,但可以使用同样的URL创建一个新的Worker。

worker.terminate();

8.3.3 HTML5 Web Workers的嵌套使用

Worker的API 能够在WebWorkers 脚本中嵌套使用,以创建于Worker:

var subWorker = new Worker("subWorker.js");

大量的worker

“如果递归生成了多个Worker都包含了同一个Javascript源文件,保守估计,你将看到一些有趣的结果,如下图所示。”

——peter

Windows 任务管理器

8.3.4 使用定时器

虽然HTML5 Web Workers不能访问window对象,但是它可以与属于window对象的Javascript定时器API 协作:

var t = setTimeout(postMessage, 2000, "delayed message");

8.3.5 示例代码

为完整起见。代码清单8-2与代码清单8-3中展示了上述页面及其Web Workers JavaScript文件的源代码。

代码清单8-2 使用HTML5 Web Workers的HTML页面代码

<!DOCTYPE html>
<title>Simple HTML5 Web Workers Example</title>
<link rel="stylesheet" href="styles.css">
<h1>Simple HTML5 Web Workers Example</h1>
<p id="support">Your browser does not support HTML5 Web Workers.</p>
<button id="stopButton" >Stop Task</button>
<button id="helloButton" >Post a Message</button>
<script>
function stopWorker() {
    worker.terminate();
}
function messageHandler(e) {
    console.log(e.data);
}
function errorHandler(e) {
    console.warn(e.message, e);
}
function loadDemo() {
    if (typeof(Worker) !== "undefined") {
        document.getElementById("support").innerHTML =
        "Excellent! Your browser supports HTML5 Web Workers";
        worker = new Worker("echoWorker.js");
        worker.addEventListener("message", messageHandler, true);
        worker.addEventListener("error", errorHandler, true);
        document.getElementById("helloButton").onclick = function() {
            worker.postMessage("Here's a message for you");
        }
        document.getElementById("stopButton").onclick = stopWorker;
    }
}
window.addEventListener("load", loadDemo, true);
</script>

代码清单8-3 HTML5 Web Worker JavaScript文件

function messageHandler(e) {
    postMessage("worker says: " + e.data + " too");
}
addEventListener("message", messageHandler, true);

8.4 构建HTML5 Web Worker应用

刚才我们讨论的是Web Workers API的各种用法。Web Workers API到底有多强大?现在我们通过建立一个HTML5 Web Workers应用来演示。我们将在这个应用中实现一个带有图像模糊过滤器的Web 页面,过滤动作将由多个Web Workers并行执行。图8-4显示了该应用程序的执行效果。

图8-4 基于HTML5 Web Worker的图像模糊过滤器页面

图8-4 基于HTML5 Web Worker的图像模糊过滤器页面

这个应用程序首先从canvas向多个Web Worker(可以指定数量)发送图像数据,然后Web Workers使用box-blur过滤对这些图像数据进行处理。处理时间大约是几秒钟,取决于图像大小和可用的计算资源(即使电脑配备高速CPU. 也可能因为巳加载了其他进程,导致需要更多的时钟周期来执行JavaScript)。

Web Workers承担了所有繁重的任务,因此不存在弹出脚本运行缓慢警告的风险,也不需要手动将任务分割成多份执行——在不能使用Web Workers的情况下,这是必须得考虑的事情。

8.4.1 编写blur.js辅助脚本

如代码清单8-4所示,在blur.js 应用脚本中,我们可以直接使用模糊过滤器,使之一直循环运行直到将输入数据全部处理完。

码清单8-4 blur.js文件中JavaScript box-blur过滤器的实现

function inRange(i, width, height) {
    return ((i>=0) && (i < width*height*4));
}
function averageNeighbors(imageData, width, height, i) {
    var v = imageData[i];
    // cardinal directions
    var north = inRange(i-width*4, width, height) ? imageData[i-width*4] : v;
    var south = inRange(i+width*4, width, height) ? imageData[i+width*4] : v;
    var west = inRange(i-4, width, height) ? imageData[i-4] : v;
    var east = inRange(i+4, width, height) ? imageData[i+4] : v;
    // diagonal neighbors
    var ne = inRange(i-width*4+4, width, height) ? imageData[i-width*4+4] : v;
    var nw = inRange(i-width*4-4, width, height) ? imageData[i-width*4-4] : v;
    var se = inRange(i+width*4+4, width, height) ? imageData[i+width*4+4] : v;
    var sw = inRange(i+width*4-4, width, height) ? imageData[i+width*4-4] : v;
    // average
    var newVal = Math.floor((north + south + east + west + se + sw + ne + nw + v)/9);
    if (isNaN(newVal)) {
        sendStatus("bad value " + i + " for height " + height);
        throw new Error("NaN");
    }
    return newVal;
}
function boxBlur(imageData, width, height) {
    var data = [];
    var val = 0;
    for (var i=0; i<width*height*4; i++) {
        val = averageNeighbors(imageData, width, height, i);
        data[i] = val;
    }
    return data;
}

简而言之。该算法通过求附近像素值的平均值对图像进行模糊处理。对于一个有着百万级像素的大图像而言,需要执行相当长的时间。在UI线程中,运行这样的循环是极不合适的。即使不弹出脚本运行缓慢的警告,在循环终止之前,用户界面也无法响应用户的其他操作。不过,利用Web Workers在后台执行计算倒是可以接受的。

8.4.2 编写blur.html应用页面

代码清单8-5是调用Web Workers的HTML页面代码。为了方便说明,撞该HTML示例也化繁为简了。我们的目的不在于建立漂亮的界面,而是通过搭建一个简洁的框架,演示如何控制WebWorkers并实际运行。应用程序的页面嵌入了canvas元素来显示输入的图像。页面上还有一组按钮,包括开始模糊(Blur、停止模糊(Stop Workers) ,重置图像(Reload) 和指定生成的Worker数量(number of workers)。

代码清单8-5 blur.html页面代码

<!DOCTYPE html>
<title>HTML5 Web Workers</title>
<link rel="stylesheet" href = "styles.css">
<h1>HTML5 Web Workers</h1>
<p id="status">Your browser does not support HTML5 Web Workers.</p>
<button id="startBlurButton" disabled>Blur</button>
<button id="stopButton" disabled>Stop Workers</button>
<button onclick="document.location = document.location;">Reload</button>
<label for="workerCount">Number of Workers</label>
<select id="workerCount">
    <option>1</option>
    <option selected>2</option>
    <option>4</option>
    <option>8</option>
    <option>16</option>
</select>
<div id="imageContainer"></div>
<div id="logOutput"></div>

接下来在blur.html中摇加添加创建Worker的代码。我们通过传递IavaScript文件的URL来实例化每个Worker对象。每个实例化的worker运行的代码相同,分别负责处理输入图像的不同部分:

function initWorker(src) {
    var worker = new Worker(src);
    worker.addEventListener("message", messageHandler, true);
    worker.addEventListener("error", errorHandler, true);
    return worker;
}

最后为blur.html文件增加错误处理的代码。这样,在worker发生错误的时候,页面不会毫无反应,而是显示相应的错误信息。我们的示例应该不会遇到任何问题,但在实际开发中监听错误事件是个好习惯,而且对调试工作的意义也很大。

function errorHandler(e) {
    log("error: " + e.message);
}

8.4.3 编写blurworker.js

现在,我们将worker用来与页面同学的代码添加到blurworker.js文件中(如代码清单8-6所示)。Web workers完成运算即可使用postmessage通知页面。我们将利用这个信息更新主页上的图像。Web workers创建完成后,会等待包含图像数据和开始模糊指令的消息。此类消息是一个Javascript对象,其中包含有消息类型和数值数组形式的图像数据。

代码清单8-6 在blurworker.js文件中发送和处理图像数据

function sendStatus(statusText) {
    postMessage({"type" : "status",    "statusText" : statusText});
}
function messageHandler(e) {
    var messageType = e.data.type;
    switch (messageType) {
        case ("blur"):
        sendStatus("Worker started blur on data in range: " +
        e.data.startX + "-" + (e.data.startX+e.data.width));
        var imageData = e.data.imageData;
        imageData = boxBlur(imageData, e.data.width, e.data.height, e.data.startX);
        postMessage({"type" : "progress",
        "imageData" : imageData,
        "width" : e.data.width,
        "height" : e.data.height,
        "startX" : e.data.startX
        });
        sendStatus("Finished blur on data in range: " +
        e.data.startX + "-" + (e.data.width+e.data.startX));
        break;
        default:
        sendStatus("Worker got message: " + e.data);
    }
}
addEventListener("message", messageHandler, true);

8.4.4 与Web Worker通信

在blurworker.js文件中,我们可以通过给Worker发送一些代表模糊任务的数据和参数来使用Worker,方法是使用postMessage函数发送一个JavaScript对象。这个JavaScript对象包含了worker负责处理的RGBA图像数据阵列、图像的尺寸和像索范围。每个Worker根据接收到的信息分别对图像的不同部分进行处理:

function sendBlurTask(worker, i, chunkWidth) {
    var chunkHeight = image.height;
    var chunkStartX = i * chunkWidth;
    var chunkStartY = 0;
    var data = ctx.getImageData(chunkStartX, chunkStartY,
    chunkWidth, chunkHeight).data;
    worker.postMessage({
        'type' : 'blur',
        'imageData' : data,
        'width' : chunkWidth,
        'height' : chunkHeight,
        'startX' : chunkStartX
    });
}

CANVAS图像数据

“postMessage可以对imagedata对象进行高效序列化,以便通过canvasAPI 使用。不过一些支持Worker和postMessage API的浏览器也许还不能支持postMessage的这种扩展的序列化能力。例如, Firefox 3.5不能通过postMessage传送imageData对象,不过未来版本可能会提供此支持。

因此,在本章中介绍的图像处理示例中,以传送imageData . data (数据序列化方式向JavaScript数组一样)的方式代替传送imageData对象本身。在Web workers执行运算任务时,它们同时将其状态和结果返回到页面上。代码清单8-6显示了数据如何在经模糊过滤器处理之后从Worker传送到页画上。同样地,消息包含了一个JavaScript 对象,这个对象中含有图像数据和标记处理范围的坐标信息。”

——Frank

HTML 页面上的消息处理程序接收上述数据,并用新的像素值更新canvas。处理后的图像数据到达时, HTML页面即时显示结果。现在,我们创建了一个可以处理图像的示例应用,并且具有利用多核CPU的潜在优势。此外,Web Workers执行时,程序也不会锁定用户界面。不会让用户界面停止响应。应用执行时的状态见图8-5。

8-6

8.4.5 运行程序

为了让示例运行起来。blur.html页面需要部署在一个Web服务器中{例如Apache或者时python的SimpleHTTPserver) 。以Python的SimpleHTTPserver为例,其部署步骤如下。

  • 安装时Python。
  • 打开包含示例文件(blur.html)的目录。

(3) 输入命令启动Python:

Python-m simplehttpserver 9999

  • 打开浏览器,输入http://localhost:9999/blur.btml。应谁就可以看到图8-5所示的页面了。

8.4.6 示例代码

为完整起见,代码清单8-7、代码清单8-8和代码清单8-9列出了示例的完整代码。

代码清单8-7 blur.html文件的内容

<!DOCTYPE html>
<title>HTML5 Web Workers</title>
<link rel="stylesheet" href = "styles.css">
<h1>HTML5 Web Workers</h1>
<p id="status">Your browser does not support HTML5 Web Workers.</p>
<button id="startBlurButton" disabled>Blur</button>
<button id="stopButton" disabled>Stop Workers</button>
<button onclick="document.location = document.location;">Reload</button>
<label for="workerCount">Number of Workers</label>
<select id="workerCount">
    <option>1</option>
    <option selected>2</option>
    <option>4</option>
    <option>8</option>
    <option>16</option>
</select>
<div id="imageContainer"></div>
<div id="logOutput"></div>
<script>
var imageURL = "example2.png";
var image;
var ctx;
var workers = [];
function log(s) {
    var logOutput = document.getElementById("logOutput");
    logOutput.innerHTML = s + "<br>" + logOutput.innerHTML;
}
function setRunningState(p) {
    // while running, the stop button is enabled and the start button is not
    document.getElementById("startBlurButton").disabled = p;
    document.getElementById("stopButton").disabled = !p;
}
function initWorker(src) {
    var worker = new Worker(src);
    worker.addEventListener("message", messageHandler, true);
    worker.addEventListener("error", errorHandler, true);
    return worker;
}
function startBlur() {
    var workerCount = parseInt(document.getElementById("workerCount").value);
    var width = image.width/workerCount;
    for (var i=0; i<workerCount; i++) {
        var worker = initWorker("blurWorker.js");
        worker.index = i;
        worker.width = width;
        workers[i] = worker;
        sendBlurTask(worker, i, width);
    }
    setRunningState(true);
}
function sendBlurTask(worker, i, chunkWidth) {
    var chunkHeight = image.height;
    var chunkStartX = i * chunkWidth;
    var chunkStartY = 0;
    var data = ctx.getImageData(chunkStartX, chunkStartY,
    chunkWidth, chunkHeight).data;
    worker.postMessage({
        'type' : 'blur',
        'imageData' : data,
        'width' : chunkWidth,
        'height' : chunkHeight,
        'startX' : chunkStartX
    });
}
function stopBlur() {
    for (var i=0; i<workers.length; i++) {
        workers[i].terminate();
    }
    setRunningState(false);
}
function messageHandler(e) {
    var messageType = e.data.type;
    switch (messageType) {
        case ("status"):
        log(e.data.statusText);
        break;
        case ("progress"):
        var imageData = ctx.createImageData(e.data.width, e.data.height);
        for (var i = 0; i<imageData.data.length; i++) {
            var val = e.data.imageData[i];
            if (val === null || val > 255 || val < 0) {
                log("illegal value: " + val + " at " + i);
                return;
            }
            imageData.data[i] = val;
        }
        ctx.putImageData(imageData, e.data.startX, 0);
        // blur the same tile again
        sendBlurTask(e.target, e.target.index, e.target.width);
        break;
        default:
        break;
    }
}
function errorHandler(e) {
    log("error: " + e.message);
}
function loadImageData(url) {
    var canvas = document.createElement('canvas');
    ctx = canvas.getContext('2d');
    image = new Image();
    image.src = url;
    document.getElementById("imageContainer").appendChild(canvas);
    image.onload = function(){
        canvas.width = image.width;
        canvas.height = image.height;
        ctx.drawImage(image, 0, 0);
        window.imgdata = ctx.getImageData(0, 0, image.width, image.height);
        n = ctx.createImageData(image.width, image.height);
        setRunningState(false);
        log("Image loaded: " + image.width + "x" + image.height + " pixels");
    };
}
function loadDemo() {
    log("Loading image data");
    if (typeof(Worker) !== "undefined") {
        document.getElementById("status").innerHTML = "Your browser supports HTML5 Web Workers";
        document.getElementById("stopButton").onclick = stopBlur;
        document.getElementById("startBlurButton").onclick = startBlur;
        loadImageData(imageURL);
        document.getElementById("startBlurButton").disabled = true;
        document.getElementById("stopButton").disabled = true;
    }
}
window.addEventListener("load", loadDemo, true);
</script>

代码清单8-8 blurworker.js文件内容

importScripts("blur.js");
function sendStatus(statusText) {
    postMessage({"type" : "status",
    "statusText" : statusText}
    );
}
function messageHandler(e) {
    var messageType = e.data.type;
    switch (messageType) {
        case ("blur"):
        sendStatus("Worker started blur on data in range: " +
        e.data.startX + "-" + (e.data.startX+e.data.width));
        var imageData = e.data.imageData;
        imageData = boxBlur(imageData, e.data.width, e.data.height, e.data.startX);
        postMessage({"type" : "progress",
        "imageData" : imageData,
        "width" : e.data.width,
        "height" : e.data.height,
        "startX" : e.data.startX
        });
        sendStatus("Finished blur on data in range: " +
        e.data.startX + "-" + (e.data.width+e.data.startX));
        break;
        default:
        sendStatus("Worker got message: " + e.data);
    }
}
addEventListener("message", messageHandler, true);

代码清单8-9 blur.js文件内容

function inRange(i, width, height) {
    return ((i>=0) && (i < width*height*4));
}
function averageNeighbors(imageData, width, height, i) {
    var v = imageData[i];
    // cardinal directions
    var north = inRange(i-width*4, width, height) ? imageData[i-width*4] : v;
    var south = inRange(i+width*4, width, height) ? imageData[i+width*4] : v;
    var west = inRange(i-4, width, height) ? imageData[i-4] : v;
    var east = inRange(i+4, width, height) ? imageData[i+4] : v;
    // diagonal neighbors
    var ne = inRange(i-width*4+4, width, height) ? imageData[i-width*4+4] : v;
    var nw = inRange(i-width*4-4, width, height) ? imageData[i-width*4-4] : v;
    var se = inRange(i+width*4+4, width, height) ? imageData[i+width*4+4] : v;
    var sw = inRange(i+width*4-4, width, height) ? imageData[i+width*4-4] : v;
    // average
    var newVal = Math.floor((north + south + east + west + se + sw + ne + nw + v)/9);
    if (isNaN(newVal)) {
        sendStatus("bad value " + i + " for height " + height);
        throw new Error("NaN");
    }
    return newVal;
}
function boxBlur(imageData, width, height) {
    var data = [];
    var val = 0;
    for (var i=0; i<width*height*4; i++) {
        val = averageNeighbors(imageData, width, height, i);
        data[i] = val;
    }
    return data;
}

8.5 小结

本章,我们讨论如何使用WebWorkers搭建具有后台处理能力的Web应用程序。首先,我们了解了WebWorkers的工作机制,说明了编写本书时各浏览器的支持情况。然后,我们讨论了如何使用API创建worker,以及如何在一个Worker与生成它的上下文之间通信。最后,我们演示了如何使用Web workers构建Web应用程序。下一章中。我们将演示如何通过HTML5 保存数据的本地副本,以此来减少应用的网络开销。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

支持 Markdown 语法,需要帮助?

目前还没有任何评论,快来抢沙发吧!