Web Worker && postMessage && onMessage 使用教程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web Worker && postMessage && onMessage 使用教程相关的知识,希望对你有一定的参考价值。

参考技术A **转自博客原文连接: https://tong-h.github.io/2019/04/21/webworker/
**

最近使用 iframe 的时候想要获取 iframe 文档信息的时候遇到了跨域问题,最后使用 postmessage 做父子页面通信解决需求也顺便学习了下 webworker 的使用
webWoker 使用依赖 postMessage() 和 onMessage(), 所以先说这两个吧

  message: 发送的数据,不限类型,因为他自己会序列化
  targetOrigin:通过窗口的origin属性指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI
  transfer:可选参数;一个 Transferable 对象([什么是Transferable[( https://developer.mozilla.org/zh-CN/docs/Web/API/Transferable) ),和message 同时传递的,对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权

  data:接收的数据对象,对应 postMessage 的 message 参数
  origin:消息发送方窗口的 origin,字符串由 协议、“://“、域名、“ : 端口号”拼接而成
  source:对发送消息的窗口对象的引用

在使用 worker 的 js文件里

在 worker.js文件

如果你现在没有条件加载网络上的文件,可以使用 URL.createObjectURL 方法建立缓存 URL
可以试着运行一下面两个页面感受一下

可以运行一下这个页面,一个普通的 for 循环,因为数字太大运行时会有明显的卡顿

这个可以使用 URL.createObjectURL 方法建立缓存 URL

postMessage:MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
webworker:MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
阮一峰: http://www.ruanyifeng.com/blog/2018/07/web-worker.html

HTML5( Web Worker ) & 微信小程序多线程

随着互联网的发展,技术也在不断的更新,2014年10月29日万维网联盟发布HTML5语言标准,HTML5推出了很多API例如Geolocation(地理定位)Storage(本地存储)Cache Manifest(应用程序缓存)Server-Sent(自动获取来自服务器的更新)WebSocket(及时通讯)indexedDB(本地数据库),还有一个就是接下来要说的Web Worker(多线程)

学习JavaScript的人都知道JavaScript语言不如javaC#等,其他强类型语言使用的是多线程处理程序,JavaScript使用单线程处理程序。

JavaScript为什么不适用多线程

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

进行&线程

进程

进程是指在操作系统中正在运行的一个应用程序

线程

线程是指进程内独立执行某个任务的一个单元。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)。

什么是Web Worker

web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行。

一般来说Javascript和页面的UI会共用一个线程,所以当点击一个按钮开始运行Javascript后,在这段代码运行完毕之前,页面是无法响应用户操作的,换句话来说就是被“冻结”了,即使是异步的也会被冻结。如下面这段代码。

window.onload = () => {
   alert(1)
   setInterval(() => {
       console.log(1)
   },500)
}

上面这段代码在页面加载完成以后,先执行alert然而,alert页面进入了无法相应的状态,换句话来说,下面的代码就被冻结了,只有把alert确定以后才会执行下面的定时器。

而这段代码可以交给Web Worker在后台运行,那么页面在Javascript运行期间依然可以响应用户操作。后台会启动一个worker线程来执行这段代码,用户可以创建多个worker线程。

Web Worker提供了什么

  1. 加载一个JavaScript文件,进行大量的复杂计算,而不挂起主进程,并通过postMessageonMessage进行通信。

  2. 可以再Worker中通过importScript()方法加载JavaScrip脚本

  3. 可以使用setTimeOut()setInterVal()clearTimeout()clearInterVal()等。

  4. 可以使用XMLHttpRequest进行请求。

  5. 可以访问navigator的部分属性

  6. 可以使用JavaScript核心对象

Web Worker限制

  1. 不能跨域加载JavaScript文件。

  2. Worker内代码不能访问DOM。

  3. 各个浏览器对Worker的实现还没有完全完善,不是每个浏览器都能支持这一新特性。

  4. 使用Web Workers加载数据没有JSONAjax加载数据高效。

Web Worker主要成员

在使用Web Worker之前,应该先熟悉一下线程中可用的变量,函数和类。在线程中调用JavaScript函数中的所有,可使用类和函数如下:

  1. self:self关键值用来表示本线程范围内的作用域。

  2. postMessage:向创建线程的原窗口发送消息。

  3. navigator:与window.navigator对象类似,具有appNameplatformuserAgentappVersion属性。他们可以用来识别浏览器的标识符。

  4. sessionStorage/localStorage:在线程中可以使用web Storage

  5. XMLHttpRequest:在线程中可以处理Ajax请求。

  6. setTimeOut/setInterVal:在线程中可以实现定时处理。

  7. close:结束本线程。

  8. eval()/isNaN/escape()等:可以使用所有JavaScript核心函数。

  9. object:可以创建和使用本地对象。

  10. WebSockets:可以使用Web Sockets API向服务器发送和接收消息。

浏览器兼容性

浏览器 说明
IE 不支持
firefox 3.5以及以上的版本
Opera 10.6及以上的版本
Chrome 3.0及以上的版本
Safari 4.0及以上的版本
# 兼容性检测
let isWorker = () => {
   let flag = false;
   if(typeof(Worker)!=="undefined"){
       flag =  true;
   }
   return flag;
}

Web Worker简单应用

简单看一下代码是怎么实现的。需要注意的是一定要在服务器环境下才能正常使用Web Worker。

index.html

<html>  
 <head>  
 <script type="text/javascript">  
   var worker = new Worker('worker.js');  
   var obj = {"first":1, "second":2};  
   worker.postMessage(obj);  
   worker.onmessage = function (event) {  
     console.log(event.data);  
   }  
   function postMsg(){  
     if (worker)  
       worker.postMessaage(obj);  
   }  
 
</script>  
 </head>  
 <body>  
    <button onclick="postMsg()">post</button>  
 </body>  
</html>  

worker.js

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);  
}  

运行代码:点击查看案例

Web Worker运行流程

  1. 执行到"var worker = new Worker('worker.js')“时,在内核中构造WebCore::JSWorker对象(JSBbindings层)以及对应的WebCore::Worker对象(WebCore模块);

  2. 执行worker.postMessage(),向worker线程发送JSON格式化的消息数据; 因为这个时候,worker线程还没有创建,所以消息数据放在一个临时消息队列中;

  3. worker.js异步加载完成后,创建并启动worker线程,并将临时消息队列中的消息数据copy到woker对应的WorkerRunLoop的消息队列中;

  4. worker线程创建完成后,开始处理WorkerRunLoop的消息队列中所保存的消息;

  5. woker线程发送消息到主线程;

  6. 主线程收到worker线程发送的消息,执行onMessage();

Web Worker案例

生成200个100以内随机数,筛选出能被3整除的,并展示到页面。

index.html

let $ = (id) => {
   return document.getElementById(id);
}
window.onload = () => {
   let arr = [];
   for(let i=0;i<200;i++){
       arr.push(Number.parseInt(Math.random()*100));
   }
   let worker = new Worker("/www/worker/worker2.js");
   worker.postMessage(JSON.stringify(arr));
   worker.onerror = (err) => {
       console.log(err);
   }
   worker.onmessage = (event) => {
       let arr = event.data;
       arr.map((el,i) => {
           let li = document.createElement("li");
           li.innerHTML = el;
           $("ul").appendChild(li);
       })
   }
}

worker.js

onmessage = (event) => {
   let arr = JSON.parse(event.data);
   let a = arr.filter((el,i) => {
       return el%3 === 0;
   })
   postMessage(a);  
}

运行代码:点击查看案例

共享线程

共享线程是为了避免线程的重复创建和销毁过程,降低了系统性能的消耗,共享线程SharedWorker可以同时有多个页面的线程链接。

var worker = new SharedWorker("sharedworker.js");

共享线程也使用了message事件监听线程消息,但使用SharedWorker对象的port属性与线程通信如下。

worker.port.onmessage = function(e){
   ...
}

同时我们也可以使用SharedWorker对象的port属性向共享线程发送消息如下。

worker.port.postMessage("message");

小结

在写这篇文章之前看了很多博客,同样也看了 ++HTML5+CSS3从入门到精通++ 这本书,很多博客都说可以进行线程嵌套,一定要记住,是不能进行线程嵌套的,就是在 worker.js 文件里面再次 new Worker 这样的话会报错。原因是Worker对象属于window,所以在线程文件中使用Worker对象会抛出错误Worker is not defined

Web Worker可以在后台完成复杂的数据计算。这是其最有利的地方。所以一定要合理的使用这一特点。不是任何时候都适用Web Worker,否则会有些小题大做,给维护带来没有必要的麻烦。

微信小程序多线程

创建一个 Worker 线程,并返回 Worker 实例,目前限制最多只能创建一个 Worker,创建下一个 Worker 前请调用 Worker.terminate

微信小程序的多线程和H5多线程是类似的,唯一的区别就是,小程序多线程处理无需服务器环境即可运行了。

scriptPath 为 worker 的入口文件路径,需填写绝对路径。

app.js

const worker = wx.createWorker('workers/request/index.js');
worker.postMessage({
 msg: 'hello worker'
})
worker.onMessage(function (res) {
 console.log(res)
})
worker.terminate

worker.js

worker.onMessage(function (res) {
 console.log(res.msg)
})
worker.postMessage("线程返回的数据")

总结

当我们创建一个新的worker时,改代码会运行在一个全新的javascript的环境中运行,是完全和创建worker的脚本隔离,这时我们可以把创建新worker的脚本叫做主线程,而被创建的新的worker叫做子线程。

但是我们所开启的新的worker也就是子线程,并不支持操作页面的DOM。

HTML5 Web Worker 需要服务器环境才能正常使用,使用线程文件和worker文件必须在同一个域下面。否则无法运行。

这个很重要。


--- End ---





 GitHub项目,喜欢的小伙伴Start一下吧:
v-scroller :基于VUE开发的一个移动端滚动插件,作者:alan chen.其功能非常强大,支持上拉加载,下拉刷新。滚动层嵌套。支持npm下载。欢迎小伙伴start支持一波。飞机票:https://github.com/alanchenchen/v-scroller



邦邦堂

前端邦邦堂是一群初入IT编程的人共同组成。用意是互帮互助,共同成长。加入邦邦堂学习群请关注[前端邦邦堂]公众号回复“加群”。投稿请发邮箱到bbt_hello@163.com 


以上是关于Web Worker && postMessage && onMessage 使用教程的主要内容,如果未能解决你的问题,请参考以下文章

HTML5( Web Worker ) & 微信小程序多线程

模块类型 Web Worker 中多个脚本的高效动态导入

断言失败:第 24 行 pos 15:'color != null && color.alpha == 0xFF':不正确

Express之get,pos请求参数的获取

xml POS&COUNT

智能POS如何获取日志&数据库文件