关于js的单多线程问题

一、为啥js是单线程

一个简单的原因就是,js在设计之初只是进行一些简单的表单校验,这完全不需要多线程,单线程完全可以胜任这项工作。即便后来前端发展迅速,承载的能力越来越多,也没有发展到非多线程不可的程度。

还有一个主要的原因,如果js是多线程的,在运行时多个线程同时对DOM元素进行操作,那具体以哪个线程为主?由此可见,线程的调度问题是一个比较复杂的问题。

HTML5新的标准中允许使用new Worker的方式来开启一个新的线程,去运行一段单独的js文件脚本。

但是在这个新线程中严格的要求了可以使用的功能,比如说他只能使用ECMAScript, 不能访问DOM和BOM。这也就限制死了多个线程同时操作DOM元素的可能。

二,关于web Worker实现多线程

web Worker可以分为以下两种:

1.专用线程(Dedicated Web Worker),仅能被创建它的脚本所使用(一个专用线程对应一个主线程),一般默认情况下的web worker都是指专用线程。

2.共享线程(Shared Web Worker),能够在不同的脚本中使用(一个共享线程对应多个主线程)。


主要用途

也是用于将耗时的数据处理操作从主线中剥离出来,减轻主线程的压力。

懒加载|、文本分析、流媒体数据处理、canvas 图形绘制、图像处理等等都可以


限制

初步介绍的时候也提及过,它是无法访问 DOM 节点的

虽然Web Worker 的运行不会影响主线程,但与主线程交互时仍受到主线程单线程的瓶颈制约。如果 Worker 线程频繁与主线程进行交互,主线程由于需要处理交互,仍可能使页面发生阻塞

运行在另一个上下文中,无法使用Window对象

有同源限制。共享线程可以被多个浏览上下文调用,但所有这些浏览上下文必须同源。


使用

不论共享线程还是专用线程它的构造方法都在window之内,使用前可以对浏览器的支持进行一个处理。

if (window.Worker){}

if (window.SharedWorker) {}

1.专用线程的创建

 var worker = new Worker('A.js', { name: 'B'})

第一个参数 必须。脚本位置

第二个参数 可选,指定 type、credentials、name 三个属性。

2.共享线程的创建

var sharedWorker = new SharedWorker('A.js')

第一个参数 必须。脚本位置

第二个参数 可选,指定 type、credentials、name 三个属性。

【 Web Worker 有同源限制,所以在本地调试的时候也需要通过启动本地服务器的方式访问,使用 file:// 协议直接打开的话将会抛出异常】


数据传递

Worker 线程和主线程都通过postMessage()方法发送消息,通过onmessage事件接收消息。

在这个过程中数据并不是被共享的,而是被复制的。

Error和Function对象不能被结构化克隆算法复制,如果尝试这么做的话会导致抛出DATA_CLONE_ERR的异常。

另外,postMessage()一次只能发送一个对象, 多个参数可以将参数包装为数组或对象再进行传递。

主线程代码:

let worker=new Worker('worker.js');

worker.postMessage([10,24]);

worker.onmessage=function (e){ console.log(e.data)}

worker线程代码

onmessage = function (e) { 

    console.log(e);

     if (e.data.length > 1) { 

            setTimeout(function (){ postMessage(e.data[1] - e.data[0]) }  ,1000)

    }

}


在 Worker 线程中,self 和 this 都代表子线程的全局对象。

对于监听 message 事件,以下的四种写法是等同的:

self.addEventListener('message', function (e) {})

this.addEventListener('message', function (e) {})

addEventListener('message', function (e) {})

 onmessage = function (e) {}


主线程通过MessagePort 来访问worker线程。专用线程的 port 会在线程创建时自动设置,并且不会暴露出来。共享线程在传递消息之前,端口必须处于打开状态。start() 方法是与 addEventListener 配套使用。选择 onmessage 进行事件监听,那么将隐含调用 start() 方法。

// 主线程var sharedWorker = new SharedWorker('shared-worker.js') 

sharedWorker.port.onmessage = function(e) { // 业务逻辑 }



var sharedWorker = new SharedWorker('shared-worker.js') 

sharedWorker.port.addEventListener('message', function(e) { // 业务逻辑 }, false) 

sharedWorker.port.start() // 需要显式打开


在传递消息时,postMessage() 方法和 onmessage 事件必须通过端口对象调用。另外,在 Worker 线程中,需要使用 onconnect 事件监听端口的变化,并使用端口的消息处理函数进行响应。

// 主线程sharedWorker.port.postMessage([10, 24])sharedWorker.port.onmessage = function (e) { console.log(e.data)}// Worker 线程onconnect = function (e) { let port = e.ports[0] port.onmessage = function (e) { if (e.data.length > 1) { port.postMessage(e.data[1] - e.data[0]) } }}



关闭 Worker

主线程中使用terminate()方法;

Worker 线程中使用close()方法。

推荐的用法是在worker线程中关闭,防止意外关闭正在运行的 Worker 线程,Worker 线程一旦关闭之后的Worker 将不再响应。



错误处理

可以在线程中设置 onerror 和 onmessageerror 的回调函数对错误进行处理。

// 主线程

worker.onerror = function () {}

// 主线程使用专用线程

worker.onmessageerror = function () {}

// 主线程使用共享线程

worker.port.onmessageerror = function () {}

// worker 线程

onerror = function () {}



加载外部脚本

使用importScripts() 方法

importScripts('A.js') 

importScripts(B.js')

 importScripts(A.js', 'B.js')


子线程

worker可以生成子worker,但是必须同源,且子worker的中的中的 URI 要相对于父worker的地址进行解析。


嵌入式Worker

可以通过Bolb()将页面中的Worker代码进行解析。

<script id='worker' type="javascript/worker">

//这段代码不会被 JS 引擎直接解析,因为类型是 'javascript/worker'

// 在这里写 Worker 线程的逻辑

</script>

<script>

var workerScript = document.querySelector('#worker').textContent

var blob = new Blob(workerScript, {type: "text/javascript"})

var worker = new Worker(window.URL.createObjectURL(blob))

</script>



关于 postMessage

Web Worker 中,Worker 线程和主线程之间使用结构化克隆算法(The structured clone algorithm)进行数据通信。

这是一种同过递归输入对象构建克隆的算法,通过保存之前访问过的映射,避免无线遍历循环。

worker提出了Transferable Objects 的概念,防止因为数据传输量过大的时 所造成的性能问题。可以在数据量大的时候,将主线程中的数据直接移交给Worker线程。这种移交是彻底的,一旦数据成功转移,主线程将不能访问该数据。这个过程还是通过postMessage进行传递的。

postMessage(message, transferList)


let aBuffer = new ArrayBuffer(1) 

worker.postMessage({ data: aBuffer }, [aBuffer])



上下文

Worker 工作在一个 WorkerGlobalDataScope 的上下文中。

每一个WorkerGlobalDataScope对象都有不同的event loop。这个event loop没有关联浏览器上下文(browsing context),它的任务队列也只有事件、回调和联网的活动。

每一个WorkerGlobalDataScope都有一个closing标志,当这个标志设为true时,任务队列将丢弃之后试图加入任务队列的任务,队列中已经存在的任务不受影响。同时,定时器将停止工作,所有挂起的后台任务将会被删除。


Worker 中可以使用的函数和类

由于 Worker 工作的上下文不同于普通的浏览器上下文,因此不能访问 window 以及 window 相关的 API,也不能直接操作 DOM。Worker 中提供了WorkerNavigator和WorkerLocation接口,它们分别是 window 中Navigator和Location的子集。除此之外,Worker 还提供了涉及时间、存储、网络、绘图等多个种类的接口,以下列举了其中的一部分,更多的接口可以参考MDN 文档

时间相关:clearInterval(),clearTimeout(),setInterval(),setTimeout

Worker 相关:importScripts(),close(),postMessage()

存储相关:Cache,IndexedDB

网络相关:Fetch,WebSocket,XMLHttpRequest

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,563评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,694评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,672评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,965评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,690评论 6 413
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,019评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,013评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,188评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,718评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,438评论 3 360
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,667评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,149评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,845评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,252评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,590评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,384评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,635评论 2 380

推荐阅读更多精彩内容