乐趣区

关于前端:如何使用-Web-Worker-为-JS-创造多线程环境

Web Worker 是什么

咱们都晓得 JS 是单线程的,所有工作在一个线程上,一次只能做一件事。尽管能够通过 AJAX、定时器等能够实现 ” 并行 ”,但还是没有扭转 JS 单线程的实质,把一些简单的运算放在页面上执行,还是会导致很卡,甚至卡死

而 HTML5 规范中的 Web Worker 为 JS 发明多线程环境,容许主线程创立 Worker 线程并给它分配任务,而且在主线程执行工作的时候,worker 线程能够同时在后盾执行它的工作,互不烦扰

这让咱们能够将一些简单运算、高频输出的响应解决、大文件分片上传等放在 worker 线程解决,最初再返回给主线程。很大水平上缓解了主线程 UI 渲染阻塞的问题,页面就会很晦涩

应用 Web Worker 有几个留神点:

  • 同源限度:worker 线程运行的文件必须和主线程的脚本是 同源
  • 文件限度:worker 线程不能关上本机的文件系统 (file://), 只能加载网络文件
  • 其余限度:worker 线程 不能操作 DOM、不能应用 document、window、parent 对象和 alert、confirm 办法

但能够应用 location、navigator,不过只能只读不能改写;还能够应用XMLHttpRequest 发送 AJAX 申请;还能够应用 缓存

怎么应用 Web Worker 呢?

别离看看在主线程的页面和 worker 线程中的办法和用法

主线程中的办法和应用

创立 worker 过程,间接 new 就完事儿了,而且主线程内和 worker 线程内都 能够创立多个 worker 线程

能够传两个参数,第一个是网络脚本文件的链接,不能是本地门路,第二个是配置对象,不是必填

假如引入了一个网络文件 worker1.js

// 主线程
const worker = new Worker('http://worker1.js')

而后应用 worker.postMessage() 办法向 worker 线程发送数据,参数能够是各种数据类型,包含二进制。留神!发送过来的数据是传值,也就是拷贝关系而不是传址,所以 worker 线程外部无论怎么批改,都不会影响到主线程的数据

worker.postMessage('这是发给 worker 线程的音讯')

而后再通过 worker.onmessage 监听并接管 worker 线程解决完返回的数据

worker.onmessage = function(event){const data = event.data // 这是返回的数据}

如果 worker 线程中外部产生谬误,会触发主线程的 onerror 事件

worker.onerror( event => {console.log( 'worker 线程外部执行报错了', event)
})
// 或者
worker.onerrer = function(event){console.log( 'worker 线程外部执行报错了', event)
}
// 或者
worker.addEventListener('error', event => {console.log( 'worker 线程外部执行报错了', event)
})

最初是关掉 worker 线程,因为 worker 线程创立胜利就会始终运行,不会被动敞开,所以咱们不必的时候要被动关掉,避免浪费资源

worker.terminate() // 这样 worker 线程就敞开了

worker 线程中的办法和应用

worker 线程外部执行上下文跟主线程的执行上下文 window 不一样,是一个叫 WorkerGlobalScope 的货色,咱们能够用它或者 self 来拜访全局对象

如果主线程创立 worker 线程的时候有传第二个参数,在 worker1.js 外面,也就是在 worker 线程里能够间接这样获取,比方辨别多个 worker 线程的时候

// 主线程
const worker = new Worker('http://worker1.js', { name: 'worker1'})

// worker1.js
self.name // worker1  

worker 线程外面,须要监听并接管主线程发送过去的数据。

self.addEventListener('message', event => {
    const data = event.data // 接管到的数据 
    ...
    self.postMessage('这是解决好的数据') // 将解决好的数据发送给主线程
}, false)
// 或者 不要 self 也能够
addEventListener('message', event => {
    const data = event.data // 接管到的数据 
    ...
    self.postMessage('这是解决好的数据') // 将解决好的数据发送给主线程
}, false)

如果 worker 线程外部须要加载其余脚本,只能用 importScripts() 办法,门路还是只能网络文件,不能应用本地的

importScripts('http://worker2.js', 'http://worker3.js') // 能够引入多个

worker 线程外部也能够应用 onerror 监听谬误,和主线程应用形式一样。

worker 线程外部也能够敞开 worker 线程,办法和主线程不一样

self.close() // 这样就从外部敞开了

worker 线程能不能间接写在主线程的页面里

能!

既然它要接管一个网络地址 URL,就给它创立一个 URL

首先 给 script 标签起一个浏览器不意识的 type,而后将这个标签里的内容转成二进制对象,而后为这个对象生成 URL,再用这个 URL 创立 worker 线程,如下

<!DOCTYPE html>
    <body>
        <div> 这是页面 </div>
        <script id="worker" type="xxxxx">
            // worker 线程
            addEventListener('message', event => {console.log(event.data) // 收到了吗
                postMessage('收到了') // 发送给主线程
            })
        </script>
        <script type="text/javascript">
            // 主线程
            const blob = new Blob([document.querySelector('#worker').textContent ])
            const url = window.URL.createObjectURL(blob)
            const worker = new Worker(url)
            worker.postMessage('收到了吗')
            worker.onmessage = event => {console.log(event.data) // 收到了
            }
        </script>
    </body>
</html>

worker 线程轮询

function createWorker(fn){const blob = new Blob([fn.toString()])
    const url = window.URL.createObjectURL(blob)
    const worker = new Worker(url)
    return worker
}

const webWorker = createWorker(function(){
    let cache;
    
    function compare(new, old){...}
    
    setInterval(()=>{fetch('/api/xxx').then(res=>{
            let data = res.data
            if(!compare(data, cache)){
                cache = data
                self.postMessage(data)
            }
        })
    },1000)
})

结语

点赞反对、手留余香、与有荣焉

参考

# Web Worker 应用教程

退出移动版