介绍
Web Worker是HTML5标准增加的多线程方案,直接使用web worker的API开发是一件比较麻烦的事:
- Worker脚本必须放在单独的文件里(虽然可以用blob inline worker,但是IE10/11不支持,并且官方也没有修改的意思)
- Worker和浏览器脚本需要通过message通信
- 不利于模块化开发,不管是AMD还是CMD模块,都不能直接通过
importScripts
载入运行
RequireJS支持web worker,可以在worker脚本一开头importScript('path/to/require.js')
,然后就愉快的一路AMD了(当然依然要message通信)。
而我最近项目里在使用的sea.js不支持,为了解决这个问题,前几天陆续向sea.js发了几个pull request,给sea.js增加了web worker支持。
在变更被merge过后,更进一步的折腾了下,把web worker的API封装了一遍,写了seajs-worker这个插件,实现了真正无缝的开发。开发者不需要关心worker的创建、生命周期管理,也不需要通过消息通信,而是直接通过清晰的异步方法调用。
链接
使用实例
使用seajs-worker开发web worker只需要3步。
第一步,配置sea.js:
1 | <!-- other sutff --> |
第二步,实现一个具体的worker类,继承自SeaWorker:
1 | # image-worker.coffee |
可以看到worker类就是一个普通CMD模块的写法,可以自由require
其它模块,只要执行路径里没有调用到web worker不允许访问的API(比如DOM)就行。
第三步,创建并调用worker。
创建:1
2Worker = require './image-worker'
worker = new Worker()
seajs-worker提供了两种调用方案。
单worker方案:
1 | worker.sepia img_src, (err, result) -> |
可以看到,再也不需要自己去实现message通信,只需要一个简单的方法调用。返回值的方法除了以上callback的方式以外,如果seajs-worker检测到了Q的存在,就会返回一个promise对象,此时就可以使用Promise/A+ Pattern避免callback的嵌套:1
2worker.sepia img_src
.then (result) -> ctx.putImageData result, 0, 0
多worker方案
仅仅使用一个worker还不足以发挥多线程的威力,seajs-worker提供了map-reduce
语法,一句话实现worker pool:
1 | # Divide image into segments |
以上代码把图像分成若干块,放到一个Array
里,然后通过map
把每块交给一个worker进行处理,指定同时最多有10个worker进行操作。完成后传回一个Array
,分别对应每块图像的处理结果,由reduce
绘制到canvas上。
这篇blog用web worker API细线了同样的功能,对比起来代码的清晰程度显而易见。
JavaScript中使用
seajs-worker为JavaScript开发者提供了几个helper方法,例:
1 | define(function(require, exports, module) { |
项目状态
目前seajs-worker的所有代码均有文档、注释,并通过了单元测试。
API语句针对CoffeeScript设计优化,用JavaScript调用暂时还不够优雅,接下来会改善这个问题。