重学JS | Web Workers让JS存在多线程环境

623 阅读2分钟

这是我参与更文挑战的第17天,活动详情查看:更文挑战

[重学JavaScript系列文章连载中...]

我们都知道JavaScript是单线程的,只有等上个任务执行完,才能执行下一个任务。当执行一些密集型或者高延迟的任务时,会造成主线程的阻塞,浏览器页面冻结。Web Worke的出现,为JavaScript创造多线程环境。允许主线程创建Worker线程,并将任务分配给worker线程。并且Worker线程是在后台运行,与主线程互不干扰。所以可以将耗时、密集型任务分配给Worker线程,等执行完成再通知主线程。

看看使用Web Worker的简单例子:

// 主线程 index.js

// 传入一个要执行的js文件,实例化Web Worker
// 此时会导致浏览器下载js文件,但不会执行
let worker = new Worker('worderDemo.js')
// 监听Worker传递过来的信息
worker.onmessage = function(event){
   const {data} = event
}
// 监听Worker内部错误
worker.onerror = function(event){
  const {
    filename,  // 文件名
    lineno,    // 代码行数
    message    // 完整的错误信息
  } = event
}
// 给Worker发送消息启动执行
worker.postMessage({
  type:'cmd',
  data:'start'
})
// 停止worker的工作,内部代码会立即停止执行,后续的过程都不会再发生
worker.terminate()
// Web Worker:  worderDemo.js 

// 监听主线程的发送消息
self.onmessage = function(event){
  const {data} = event
  
  self.postMessage({
    type:'end',
    data:'Worker处理完数据了'
  })
}

这个例子里的Worker只能为指定的页面服务,不能在页面共享的,因此也称为“专用Worker”。关于页面共享的“Worker”目前尚未有完成规范。

代码里为啥用self进行监听呢?这里看看Worker的作用域。

Worker所执行的JavaScript是完全独立的一个作用域,不与页面共享作用域,且Web Worker中的全局对象指向的是worker对象本身,所以this指向worker对象,这里self也引用worker对象。在这个特殊域中,无法访问DOM,也无法通过任何形式影响页面的外观。

当然,Web Worker本身也是一个最小化的运行环境,不过跟页面环境是没法比。存在以下可操作对象:

  1. 最小化的navigator对象,包括onLine、appName、appVersion、userAgent和platform属性
  2. 只读的location对象
  3. setTimeout()、setInterval()、clearTimeout()和clearInterval()方法
  4. XMLHTTPRequest构造函数

最后总结下几个注意点:

  1. 同源策略:分配给Worker线程运行的脚本文件,必须与主线程的脚本文件同源
  2. 脚本限制:不能执行alert()和confirm()方法,但可以使用XMLHTTPRequest
  3. 文件限制:Worker无法读取本地文件(file://),所加载的文件必须来源网络
  4. DOM限制:Worker不能操作Dom,也无法使用window/doument这些,但可以使用navigator等。
  5. 通信联系:Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

至此,简单学习了下Web Workers应用。