web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。这是HTML5的一个标准;实现上讲,浏览器为wokrer启动了新的线程,从而实现了异步操作的功能;
下面是woker的一个简单例子,在html页面中,以worker.js为源文件,创建了名为“worker”的Worker对象,通过worker.postMessage()接口向worker线程发送消息; worker线程将JSON格式化传递的两个数据相加后,再通过postMessage()接口将消息发送回页面js运行的主线程; 主线程再在onMessage()函数中处理woker线程发来的消息;
main.html
onmessage = function (event)
{
var data = event.data;
var first=data.first;
var second=data.second;
handleTask(first,second);
};
function handleTask(a, b)
{
var out = a + b;
postMessage("Worker Done! out = " + out);
}
WebKit加载并执行js的流程简单分成以下几步:
1. 执行到"var worker = new Worker('worker.js')“时,在内核中构造WebCore::JSWorker对象(JSBbindings层)以及对应的WebCore::Worker对象(WebCore模块);
2. 构造JSWorker对象的过程中,根据初始化的url地址"worker.js"发起异步加载的流程;
3. 执行worker.postMessage(),向worker线程发送JSON格式化的消息数据; 因为这个时候,worker线程还没有创建,所以消息数据放在一个临时消息队列中;
4. worker.js异步加载完成后,创建并启动worker线程,并将临时消息队列中的消息数据copy到woker对应的WorkerRunLoop的消息队列中;
5. worker线程创建完成后,开始处理WorkerRunLoop的消息队列中所保存的消息;
6. woker线程发送消息到主线程;
7. 主线程收到worker线程发送的消息,执行onMessage();
在经过一轮消息来回后,我们如果通过例子中button按钮来异步触发消息发送,那么步骤3的执行会有区别; 这个时候因为worker线程已经创建,所以消息会直接添加到WorkerRunLoop的消息队列中;
为了搞清楚整个实现机制,我们先来看一下WebKit内部worker相关的类, 其中Worker对应主页面JS中的'worker'对象(主线程中),而DedicatedWorkerThread表示worker线程; WorkerMessagingProxy关联了Worker对象与worker线程,从而实现主线程与worker线程之间的消息中转; DedicatedWorkerThread通过WorkerScriptController控制worker.js文件中的脚本在worker线程中的执行;浏览器会为worker线程创建一个独立的虚拟机环境(VM);
下面来看一下详细流程:
1. worker.js的异步加载过程如下图,加载是在创建WebCore::JSWorker和WebCore::Worker对象的时候发起的,并且是异步加载的流程,不会阻塞后续JS的执行,这也是为什么首次调用worker.postMessage()的时候,会出现worker线程还没有创建的情况;
2. worker线程的创建如下图所示。可以看出,当worker.js加载完成后,WebKit会通过中转对象WorkerMessagingProxy创建DedicatedWorkerThread对象,并启动WorkerThread; WorkerMessagingProxy保持DedicatedWorkerThread对象的指针;
3. 主线程向worker线程发送消息的流程如下,当js执行到"worker.postMessage()“时,最终会通过JS主线程虚拟机映射到JSWorker::postMessage()函数,并通过中转对象WorkerMessagingProxy将消息添加到worker的消息队列; 如果worker线程在发送消息的时候,还没有创建,我们看到有个m_queueEarlyTasks对象会临时保存当前消息,并在worker线程创建后再转移到正式的消息队列中; 否则,直接将消息添加到WorkerRunLoop管理的正式消息队列中;
4. worker线程中处理消息的流程如下,这就是worker.js中开始执行”onmessage()”的流程; 我们可以看到DedicatedWorkThread对象在自己的线程环境下的runLoop取出消息队列中的数据执行;而执行是通过EventTarget::dispatchEvent()分发并fire一个"message"类型的事件来实现的;
5. 下图是在worker线程中发送消息的流程,也是通过WorkerMessagingProxy来进行中传,最后会触发Document::postTask()函数,该函数实际上将Document::didReceiveTask()函数抛到主线程上去执行;
6. 下图是main线程上处理消息的流程, Document::didReceiveTask()在主线程上开始执行,最终也是通过dispatch并fire一个"message"类型的事件实现消息的处理;
所以总得来讲,woker的机制是通过中转对象实现消息的传递,再通过"message"事件完成消息的处理;
(转载请注明出处:http://blog.csdn.net/codigger)