GVKun编程网logo

Web Worker(webworker在前端中的应用)

1

如果您对WebWorker感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于WebWorker的详细内容,我们还将为您解答webworker在前端中的应用的相关问题,并且为您提

如果您对Web Worker感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于Web Worker的详细内容,我们还将为您解答webworker在前端中的应用的相关问题,并且为您提供关于Apache Spark:“未能启动org.apache.spark.deploy.worker.Worker”或Master、Celery 4.4.0、django 和flower:Celery worker 接收到已经被其他 worker 执行的任务它没有执行但添加到处理中、ES6+Webpack 下使用 Web Worker、Firestore 拒绝使用完全开放的安全规则 (web-app) 在 web worker 中添加操作的有价值信息。

本文目录一览:

Web Worker(webworker在前端中的应用)

Web Worker(webworker在前端中的应用)

Web Worker

学习阮一峰老师的web worker而记录下来

一概述

JavaScript语言采用的是单线程,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核CPU的出现,单线程带来很大的不方便,无法充分发挥计算机的计算能力。Web worker的作用是为JavaScript创作多线程环境,允许主线程创建worker线程,将一些任务分配给后者运行。在主线程运行的同时,worker线程在后台运行,两者互不干扰。等到worker线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被worker线程负担了,主线程(通常负责UI交互)就会很流畅,不会被阻塞或拖慢。

Worker线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮,提交表单)打断。这样有利于随时响应主线程的通信。但是这也造成了worker比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭

Web worker有以下几个使用注意点

  1. 同源限制

分配给worker线程运行的脚本文件,必须与主线程的脚本文件同源。

  1. DOM限制

Worker线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的DOM对象,也无法使用document, window, parent这些对象。但是worker线程可以使用navigator, location(只读)对象

  1. 通信联系

Worker线程和主线程不再同一个上下文环境,他们不能直接通信,必须通过消息完成

  1. 脚本限制

Worker线程不能执行alert()方法confirm()方法,但可以使用XMLHttpRequest对象发出Ajax请求

  1. 文件限制

Worker线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本必须来自网络

二.基本用法

2.1主线程

主线程采用new命令,调用Worker()构造函数,新建一个Worker线程

Var worker = new Worker(‘worker.js’)

Worker()构造函数的参数是一个脚本文件,该文件就是Worker线程所要执行的任务。由于Worker不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功,Worker就会默默地失败。

然后主线程调用worker.postMassage()方法向Worker发消息

Worker.postMessage(‘hello world’)

Worker.postMessage({method: ‘echo’}, args: [‘Work’])

Worker.postMessage()方法地参数,就是主线程传给Worker地数据,可以是各种数据类型包括二进制

接着主线程通过worker.onmessage指定监听函数,接收子线程发回来地消息

Worker.onmessage = functioin (event) {

       Console.log(event.data)

       DoSomething()

}

Function doSomething(){

       //执行任务

       Worker.postMessage(‘work done!’)

}

上面代码中,事件对象地data熟悉可以获取worker发来地数据,worker完成任务以后主线程就可以把它关掉

Worker.terminate()

2.2Worker线程

Worker线程内部需要有一个监听函数,监听message事件

Self.addEventListener(‘message’, function (e) {

       Self.postMessage(‘you said:’ + e.data)

}, false)

上面代码中,self代表子线程自身,即子线程地全局对象,因此等同于下面两种写法

//写法一

This.addEventListerer(‘message’, function (e) {

       This.postMessage(‘you said:’ + e.data)

}, false)

AddEventListener(‘message’, function(e) {

       PostMessage(‘you said:’ + e.data)

},false)

除了使用addEventListener()指定监听函数,也可以使用self.onmessage指定。监听函数地参数是一个事件对象。它地data属性包含主线程发来地数据。Self.postMessage()方法用来向主线程发送消息

根据主线程发来地数据,worker线程可以调用不同地方法

Self.addEventListener(‘message’,function(e){

       Var data = e.data

       Switch(data.cmd) {

       Case ‘start’:

              Self.postMessage(‘worker started:’ + data.msg)

              Break;

       Case ‘stop’:

              Self.postMessage(‘worker stoped:’ + data.msg)

              Self.close()

              Break;

       Default:

              Self.postMessa(‘unknown:’ + data.msg)

}

},false)

Self.close()用于再worker内部关闭自身

2.3worker加载脚本

Worker内部如果要加载其它脚本,有一个专门的方法importScript()

ImportScript(‘script.js’)

加载多个脚本

ImportScript(‘script1.js’,’script2.js’,’script3.js’)

2.4错误处理

主线程可以监听worker是否发生错误。如果发生错误worker会触发主线程的error事件

Worker.onerror(function (event) {

       Console.log([

              ‘error:line’, event.loneno,’ in ’, event.filename, ‘ : ’,event.message

].join(‘’))

})

Worker.addEventListener(‘error’, function (event){

})

Worker内部也可以监听error事件

2.5关闭worker

使用完毕,为了节省系统资源,必须关闭worker

//主线程

Worker.terminate()

//worker线程

Self.close()

三数据通信

前面说过,主线程与worker之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传地址。Worker对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是先将通信内容串行化,然后把串行化后的字符串发给worker,后者再将它还原

主线程与worker之间也可以交换二进制数据,比如file, blob, arrayBuffer等类型。也可以在线程之间发送。

//主线程

Var uInt8Array = new Uint8Array(new ArrayBuffer(10))

For (var I = 0l I < uInt8Array.length; ++i) {

       uInt8Array[i] = I * 2 // [0,2,4,6,7,…….]

}

Worker.postMessage(uInt8Array)

 

//worker线程

Self.onmessage = function (e) {

       Var uInt8Array = e.data

       PostMessage(uInt8Array.toString())

       PostMessage(uInt8Array.byteLength)

}

但是,拷贝方式发送二进制数据会造成性能问题。比如主线程向worker发送一个500MB文件,默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦。这种转移数据的方法叫做Transferable Objects。这使得主线程可以快熟把数据交给worker,对于影像处理,声音处理,3D运算等就非常方便了,不会产生性能负担

如果要直接转移数据的控制权,就要使用下面的写法

// Transferable Objects 格式

Worker.postMessage(arrayBuffer,[arrayBuffer])

 

//例子

Var ab = new ArrayBuffer(1)

Worker.posrMessage(ab,[ab])

四同页面的web worker

通常情况下,worker载入的是一个单独的JavaScript脚本文件,但是也可以载入与主线程在同一个网页的代码

<!DOCTYPE html>

<body>

       <script id=”worker” type=”app/worker”>

              AddEventListener(‘message’, function () {

                     PostMessage(‘some message’)

}, false)

       </script>

</body>

</html>

上面是一段嵌入网页的脚本,注意必须指定<script标签的type属性是一个浏览器不认识的值,上例子是app/worker

然后读取这一段嵌入页面的脚本用worker来处理

Var blob = new Blob([document.querySeletor(‘#worker’).textContent])

Var url = window.URL.createObjectURL(blob)

Var worker = new Worker(url)

 

Worker.onmessage = function (e) {

       //e.data === ‘some message’

}

上面代码中,先将嵌入网页的脚本代码,专程一个二进制对象,然后为这个二进制对象生成URL.再让worker加载这个URL.这样就做到了,主线程与worker的带都再同一个网页上面

五实例:worker线程完成轮询

有时候浏览器需要轮询服务器状态,以便第一时间得知状态改变。这个工资可以放在worker里面

Function createWorker(f){

       Var blob = new Blob([‘(’ + f.toString() + ‘)()’])

       Var url = window.URL.createObjectURL(blob)

       Var worker = new Worker(url)

       Return worker

}

 

Var pollingWorker = createWorker (function(e){

       Var cache

       Function compare (new, old) {…}

       SetIntval(function () {

              Fetch(‘/my-api-endpoint’).then(function (res){

                     Var data = res.json()

                    

                     If (!compare(data,cache)){

                            Cache = data

                            Self.postMessage(data)

}

})

},1000)

})

 

PollingWorker.onmessage = function () {

       // render data

}

PollingWorker.postMessage(‘int’)

上面代码中,worker每秒钟轮询一次数据,然后跟缓存做比较。如果不一致说明服务端有了新的变化,因此就要通知主线程

六实例:worker新建worker

Worker线程内部还能再新城worker线程(目前只有firefox浏览器支持)。下面的例子是将一个计算密集的任务分配到10个worker

主线程:

Var worker = new Worker(‘worker.js’)

Worker.onmssage = function(event){

       Document.getElementById(‘result’).textContent = event.data

}

Worker线程代码如下:

// worker.js

 

//settings

Var num_worker = 10

Var items_per_worker = 1000000

 

//start the workers

Var result = 0

Var pending_workers = num_workers

For (var I = 0; I < num_workers; i+=1) {

       Var worker = new Worker(‘core.js’)

       Worker.postMessage(I * items_worker)

       Worker.postMessage((i+1) * items_per_worker)

       Worker.onmessage = storeResult

}

 

//handle the results

Function storeResult (event) {

       Result += event.data

       Pending_worker -=1

       If (pending_worker <= 0) {

 

       PostMessage(result) // finished!

}

}

上面代码中,worker线程内部新建10个worker线程,并且依次向这10个worker发送消息,告知了计算的七点和终点。计算任务脚本的代码如下

//core.js

Var start

Onmessage = getStart

Function getStart(event){

       Start = event.data

       Onmessage = getEnd

}

 

Var end

Function getEnd(event){

       End = event.data

       Onmessage = null

       Work()

}

 

Function work() {

       Var result = 0

       For (var I = start; I < end; I += 1) {

              //perform some complex calculation here

              Result += 1

}

PostMessage(result)

Close()

}

七 API

7.1主线程

浏览器原生提供winker()构造函数,用来供主线程生成worker线程

Var myWorker = new Worker(jsURL,options)

Worker()构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵守同源策略),该参数是必须的,且只能加载JS脚本。第二个参数是配置对象,该对象可选。它的一个作用就是指定worker的名称,用来区分多个worker线程

// 主线程

Var myWorker = new Worker(‘worker.js’, {name: ‘myWorker’})

 

//worker线程

Worker()构造函数返回一个worker线程对象,用来供主线程操作worker。Worker线程对象的属性和方法如下

Worker.onerror:指定error事件的监听函数

Worker.onmessa:指定messa事件的监听函数,发怂过来的数据再Event.data属性中

Worker.onmessageerror:指定messageerror事件的监听函数。发送的数据无法序列号称字符串时,会触发这个事件

Worker.postMessage():向worker线程发送消息

Worker.termitnate():立即终止worker线程

7.2worker线程

Web worker有自己的全局对象,不是主线程的window,而是一个专门为Worker定制的全局对象,因此定义再window上面的对象和方法不是全部都可以

Worker线程有一些自己的全局属性和方法

Self.nama: worker的名字,该属性只读,由构造函数指定

Self.onmessageerror:指定messageerror事件的监听函数。发送的树无法序列号成字符串时,会触发这个事件

Self.close():关闭worker线程

Self.postMessage():向产生这个worker线程发送消息

Self.importScript():加载JS脚本

 

Apache Spark:“未能启动org.apache.spark.deploy.worker.Worker”或Master

Apache Spark:“未能启动org.apache.spark.deploy.worker.Worker”或Master

我在Ubuntu14.04上运行的Openstack上创build了一个Spark集群,内存为8gb。 我创build了两个虚拟机,每个3gb(为父操作系统保留2 GB)。 此外,我从第一台虚拟机创build一个主人和两个工人,从第二台机器创build三个工人。

spark-env.sh文件具有基本设置

export SPARK_MASTER_IP=10.0.0.30 export SPARK_WORKER_INSTANCES=2 export SPARK_WORKER_MEMORY=1g export SPARK_WORKER_CORES=1

每当我用start-all.sh部署集群,我得到“无法启动org.apache.spark.deploy.worker.Worker”,有的时候“无法启动org.apache.spark.deploy.master.Master”。 当我看到日志文件来查找错误,我得到以下

Spark命令:/ usr / lib / jvm / java-7 -openjdk-amd64 / bin / java -cp> /home/ubuntu/spark-1.5.1/sbin /../ conf /:/ home / ubuntu / spark- > 1.5.1 /组件/目标/阶-2.10 /火花组件-1.5.1-> hadoop2.2.0.jar:/home/ubuntu/spark-1.5.1/lib_managed/jars/datanucleus-api-> jdo- 3.2.6.jar:/home/ubuntu/spark-1.5.1/lib_managed/jars/datanucleus-core-> 3.2.10.jar:/home/ubuntu/spark-1.5.1/lib_managed/jars/datanucleus-rdbms – > 3.2.9.jar -xms1g -Xmx1g -XX:MaxPermSize = 256m> org.apache.spark.deploy.master.Master –ip 10.0.0.30 –port 7077 –webui-> port 8080

尝试在群集上无头执行Netlogo时发生Java错误

使用Python在unix / linux中的单个目录中的文件数量限制

带有IIS7.5的ColdFusion 9群集

新起搏器中的crm_mon命令不起作用

如何在计算机群集上运行进程时访问标准输出?

虽然我得到了失败的信息,但是主人或工人在几秒钟后仍然活着。

有人可以解释原因吗?

使用Linux虚拟服务器在MMO游戏中对区域进行负载平衡

运行数十个小时后,在远程集群上出现奇怪的“Stale file handle,errno = 116”

Windows群集 – SSH似乎失败

如何configuration“与心脏起搏器清除过期的失败计数”时间

使用qsub运行shellscript时出现''文件意外结束''和''错误导入函数定义''错误

Spark配置系统是一堆乱七八糟的环境变量,参数标志和Java属性文件。 我花了几个小时追踪相同的警告,并解开Spark初始化过程,这里是我发现的:

sbin/start-all.sh调用sbin/start-master.sh (然后sbin/start-slaves.sh )

sbin/start-master.sh调用sbin/spark-daemon.sh start org.apache.spark.deploy.master.Master ...

sbin/spark-daemon.sh start ...分叉一个调用bin/spark-class org.apache.spark.deploy.master.Master ... ,捕获生成的进程ID(PID),睡2秒,然后检查该pid的命令名是否是“java”

bin/spark-class是一个bash脚本,所以它以命令名“bash”开头,然后进入:

(重新)加载Spark环境,方法是使用bin/load-spark-env.sh

找到java可执行文件

找到合适的Spark jar

调用java ... org.apache.spark.launcher.Main ...来获取Spark部署所需的完整类路径

然后最后通过exec将控制交给java ... org.apache.spark.deploy.master.Master ,此时命令名变为“java”

如果从4.1到4.5的时间超过了2秒,那么在我以前(以及您的)体验中,在java以前从未运行过的新操作系统上,这些操作看起来几乎是不可避免的,尽管没有任何东西失败了。

奴隶们会因为同样的原因而抱怨,并且一直在等到主人真的可以使用,但是他们应该继续重试,直到他们成功地连接到主人。

我在EC2上运行了一个非常标准的Spark部署; 我用:

conf/spark-defaults.conf来设置spark.executor.memory并通过spark.{driver,executor}.extraClasspath添加一些自定义的jar spark.{driver,executor}.extraClasspath

conf/spark-env.sh来设置SPARK_WORKER_CORES=$(($(nproc) * 2))

conf/slaves列出我的奴隶

以下是我如何开始Spark部署,绕过一些{bin,sbin}/*.sh雷区/迷宫:

# on master,with SPARK_HOME and conf/slaves set appropriately mapfile -t ARGS < <(java -cp $SPARK_HOME/lib/spark-assembly-1.6.1-hadoop2.6.0.jar org.apache.spark.launcher.Main org.apache.spark.deploy.master.Master | tr '''' ''n'') # $ARGS Now contains the full call to start the master,which I daemonize with nohup SPARK_PUBLIC_DNS=0.0.0.0 nohup "${ARGS[@]}" >> $SPARK_HOME/master.log 2>&1 < /dev/null &

我仍然使用sbin/start-daemon.sh来启动从站,因为这比在ssh命令中调用nohup更简单:

MASTER=spark://$(hostname -i):7077 while read -r; do ssh -o StrictHostKeyChecking=no $REPLY "$SPARK_HOME/sbin/spark-daemon.sh start org.apache.spark.deploy.worker.Worker 1 $MASTER" & done <$SPARK_HOME/conf/slaves # this forks the ssh calls,so wait for them to exit before you logout

那里! 它假定我使用的是所有的默认端口和东西,而且我并没有像把空格放在文件名中那样愚蠢的操作,但是我认为这种方式更加简单。

总结

以上是小编为你收集整理的Apache Spark:“未能启动org.apache.spark.deploy.worker.Worker”或Master全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

Celery 4.4.0、django 和flower:Celery worker 接收到已经被其他 worker 执行的任务它没有执行但添加到处理中

Celery 4.4.0、django 和flower:Celery worker 接收到已经被其他 worker 执行的任务它没有执行但添加到处理中

如何解决Celery 4.4.0、django 和flower:Celery worker 接收到已经被其他 worker 执行的任务它没有执行但添加到处理中

我已经设置:

姜戈:3.1.3 Django_celery_result: 1.1.0 芹菜:4.4.0 amqp:2.5.2 芹菜花:最新 CELERY_RESULT_BACKEND= Django python db

我已经开始了 10 个不同的芹菜工人。

当提交任务时,芹菜工人会收到它。其中一名工人拿起它并开始处理它,其他工人也收到/接受它,这导致芹菜花的进程数增加。所有这些工作人员都在不断重试,从而导致大量的进程数。对于 3 个任务,进程数为:40+。

当一个worker已经在执行任务时,我们如何确保其他worker不会重试。

这适用于从 manage.py celery worker 执行的 celery 3.x 版本。但是芹菜版本的问题:4.4.0。

如果需要更多详细信息,请告诉我。

ES6+Webpack 下使用 Web Worker

ES6+Webpack 下使用 Web Worker

大家都知道 HTML 5 新增了很多 API,其中就包括 Web Worker,在普通的 js 文件上使用 ES5 编写相关代码应该是完全没有问题了,只需要在支持 H5 的浏览器上就能跑起来。

那如果我们需要在 ES6+Webpack 的组合环境下使用 Web Worker呢?其实也很方便,只需要注意一下个别点,接下来记录一下我踩过的坑。

至于 Web Worker 的基础知识和基本 api 我就放到最后面当给还不了解或者没有系统使用过的读者们去简单阅读一下。

1. 快速创建工程环境

假设你已经有一份 ES6+Webpack 的代码工程环境,而且是可以顺利跑起来的;如果没有,可以 clone 我的 github 仓库:github.com/irm-github/…

2. 安装及使用 worker-loader

2.1 安装依赖:

$ npm install -D worker-loader
# 或
$ yarn add worker-loader --dev
复制代码

2.2 代码中直接使用 worker-loader

// main.js
var MyWorker = require("worker-loader!./file.js");
// var MyWorker = require("worker-loader?inline=true&fallback=false!./file.js"); var worker = new MyWorker(); worker.postMessage({a: 1}); worker.onmessage = function(event) { /* 操作 */ }; worker.addEventListener("message", function(event) { /* 操作 */ }); 复制代码

优点:写 worker 逻辑的脚本文件可以任意命名,只要传进 worker-loader 中处理即可; 缺点:每引入一次 worker 逻辑的脚本文件,就需要写一次如上所示的代码,需要多写 N(N>=1) 次的 "worker-loader!"

2.3 在 webpack 的配置文件中引入 worker-loader

{
  module: {
    rules: [
      {
        // 匹配 *.worker.js
        test: /\.worker\.js$/,
        use: { loader: ''worker-loader'', options: { name: ''[name]:[hash:8].js'', // inline: true, // fallback: false // publicPath: ''/scripts/workers/'' } } } ] } } 复制代码

其中配置,可以设置 inline 属性为 true 将 worker 作为 blob 进行内联; 要注意,内联模式将额外为浏览器创建 chunk,即使对于不支持内联 worker 的浏览器也是如此;若这种浏览器想要禁用这种行为,只需要将 fallback 参数设置为 false 即可。

3. 同源策略

Web Worker 严格遵守同源策略,如果 webpack 的静态资源与应用代码不是同源的,那么很有可能就被浏览器给墙掉了,而且这种场景也经常发生。对于 Web Worker 遇到这种情况,有两种解决方案。

3.1 第一种

通过设置 worker-loader 的选项参数 inline 把 worker 内联成 blob 数据格式,而不再是通过下载脚本文件的方式来使用 worker:

App.js

import Worker from ''./file.worker.js'';
复制代码

webpack.config.js

{
  loader: ''worker-loader''
  options: { inline: true }
}
复制代码

3.2 第二种

通过设置 worker-loader 的选项参数 publicPath 来重写掉 worker 脚本的下载 url,当然脚本也要存放到同样的位置:

App.js

// This will cause the worker to be downloaded from `/workers/file.worker.js`
import Worker from ''./file.worker.js'';
复制代码

webpack.config.js

{
  loader: ''worker-loader''
  options: { publicPath: ''/workers/'' }
}
复制代码

4. devServer 模式下报错 "window is not defined"

若使用了 webpack-dev-server 启动了本地调试服务器,则有可能会在控制台报错: "Uncaught ReferenceError: window is not defined"

反正我是遇到了,找了很久未果,当时还是洗了把脸冷静下来排查问题,尝试着先后在 worker-loaderwebpack-dev-serverwebpack 的 github 仓库的 issues 里面去找,最后果然在 webpack 的 github 仓库里找到了码友的提问,官方给出了答案:

只需要在 webpack 的配置文件下的 output 下,加一个属性对:globalObject: ''this''

output: {
  path: DIST_PATH,
  publicPath: ''/dist/'',
  filename: ''[name].bundle.[hash:8].js'', chunkFilename: "[name].chunk.[chunkhash:8].js", globalObject: ''this'', }, 复制代码

5. Web Worker 出现的背景

JavaScript 引擎是单线程运行的,JavaScript 中耗时的 I/O 操作都被处理为异步操作,它们包括键盘、鼠标 I/O 输入输出事件、窗口大小的 resize 事件、定时器(setTimeoutsetInterval)事件、Ajax 请求网络 I/O 回调等。当这些异步任务发生的时候,它们将会被放入浏览器的事件任务队列中去,等到 JavaScript 运行时执行线程空闲时候才会按照队列先进先出的原则被一一执行,但终究还是单线程。

平时看似够用的异步编程(promiseasync/await),在遇到很复杂的运算,比如说图像的识别优化或转换、H5游戏引擎的实现,加解密算法操作等等,它们的不足就将逐渐体现出来。长时间运行的 js 进程会导致浏览器冻结用户界面,降低用户体验。那有没有什么办法可以将复杂的计算从业务逻辑代码抽离出来,让计算运行的同时不阻塞用户操作界面获得反馈呢?

HTML5 标准通过了 Web Worker 的规范,该规范定义了一套 api,它允许一段 js 程序运行在主线程之外的另一个线程中。工作线程允许开发人员编写能够长时间运行而不被用户所中断的后台程序, 去执行事务或者逻辑,并同时保证页面对用户的及时响应,可以将一些大量计算的代码交给web worker运行而不冻结用户界面。

5. Web Worker 的类型

之前一直认为不就那一种类型吗,哪里还会有多类型的 Worker。答案是有的,其可分为两种类型:

  1. 专用 Worker, Dedicated Web Worker
  2. 共享 Worker, Shared Web Worker

「专用 Worker」只能被创建它的页面访问,而「共享 Worker」可以在浏览器的多个标签中打开的同一个页面间共享。

在 js 代码中,Woker 类代表 Dedicated WorkerShared Worker 类代表 Shared Web Worker

下面的一些示例代码我就直接用 ES5 去写了,上面教了大家怎么使用在 ES6+Webpack 的环境下,迁移这种工作大家就当练习,多动动手。

6. 如何创建 Worker

很简单

// 应用文件 app.js
var worker = new Worker(''./my.worker.js''); // 传入 worker 脚本文件的路径即可 复制代码

7. 如何与 worker 通信

就通过两个方法即可完成:

应用文件 app.js

// 创建 worker 实例
var worker = new Worker(''./my.worker.js''); // 传入 worker 脚本文件的路径即可 // 监听消息 worker.onmessage = function(evt){ // 主线程收到工作线程的消息 }; // 主线程向工作线程发送消息 worker.postMessage({ value: ''主线程向工作线程发送消息'' }); 复制代码

worker 文件 my.worker.js

// 监听消息
this.onmessage = function(evt){ // 工作线程收到主线程的消息 }; this.postMessage({ value: ''工作线程向主线程发送消息'' }); 复制代码

8. Worker 的全局作用域

使用 Web Worker 最重要的一点是要知道,它所执行的 js 代码完全在另一作用域中,与当前主线程的代码不共享作用域。在 Web Worker 中,同样有一个全局对象和其他对象以及方法,但其代码无法访问 DOM,也不能影响页面的外观。

Web Worker 中的全局对象是 worker 对象本身,也即 thisself 引用的都是 worker 对象,说白了,就像上一段在 my.worker.js 的代码,this 完全可以换成 self,甚至可以省略。

为便于处理数据,Web Worker 本身也是一个最小化的运行环境,其可以访问或使用如下数据:

  • 最小化的 navigator 对象 包括 onLine, appName, appVersion, userAgentplatform 属性
  • 只读的 location 对象
  • setTimeout(), setInterval(), clearTimeout(), clearInterval() 方法
  • XMLHttpRequest 构造函数

9. 如何终止工作线程

如果在某个时机不想要 Worker 继续运行了,那么我们需要终止掉这个线程,可以调用在主线程 Worker 的 terminate 方法 或者在相应的线程中调用 close 方法:

应用文件 app.js

var worker = new Worker(''./worker.js'');
// ...一些操作
worker.terminate();
复制代码

Worker 文件 my.worker.js

self.close();
复制代码

10. Worker 的错误处理机制

具体来说,Worker 内部的 js 在执行过程中只要遇到错误,就会触发 error 事件。发生 error 事件时,事件对象中包含三个属性:filename, linenomessage,分别表示发生错误的文件名、代码行号和完整的错误消息。

worker.addEventListener(''error'', function (e) {
  console.log(''MAIN: '', ''ERROR'', e); console.log(''filename:'' + e.filename + ''-message:'' + e.message + ''-lineno:'' + e.lineno); }); 复制代码

11. 引入脚本与库

Worker 线程能够访问一个全局函数 importScripts() 来引入脚本,该函数接受 0 个或者多个 URI 作为参数来引入资源;以下例子都是合法的:

importScripts();                        /* 什么都不引入 */
importScripts(''foo.js'');                /* 只引入 "foo.js" */
importScripts(''foo.js'', ''bar.js''); /* 引入两个脚本 */ 复制代码

浏览器加载并运行每一个列出的脚本。每个脚本中的全局对象都能够被 worker 使用。如果脚本无法加载,将抛出 NETWORK_ERROR 异常,接下来的代码也无法执行。而之前执行的代码(包括使用 window.setTimeout() 异步执行的代码)依然能够运行。importScripts() 之后的函数声明依然会被保留,因为它们始终会在其他代码之前运行。

注意: 脚本的下载顺序不固定,但执行时会按照传入 importScripts() 中的文件名顺序进行。这个过程是同步完成的;直到所有脚本都下载并运行完毕, importScripts() 才会返回。

 

Firestore 拒绝使用完全开放的安全规则 (web-app) 在 web worker 中添加操作

Firestore 拒绝使用完全开放的安全规则 (web-app) 在 web worker 中添加操作

如何解决Firestore 拒绝使用完全开放的安全规则 (web-app) 在 web worker 中添加操作

因此,我设置了以下 Web Worker 以将数据添加到 Firestore 集合。

web worker 代码的第一部分初始化 Firebase 并获取一些引用

importScripts("https://www.gstatic.com/firebasejs/8.3.1/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.3.1/firebase-auth.js");
importScripts("https://www.gstatic.com/firebasejs/8.3.1/firebase-firestore.js");

// firebase configuration object
const firebaseConfig = {
  apiKey: "XXX",authDomain: "XXX",projectId: "XXX",storageBucket: "XXX",messagingSenderId: "XXX",appId: "XXX"
};
// initialize firebase
firebase.initializeApp(firebaseConfig);
// get reference to db
const db = firebase.firestore();
// get reference to collection
const collection = db.collection("data");

以下函数在网络工作者内部执行实际上传(至少在我的意图中)

// function to handle document upload to firestore
const uploadData = async document => {
  // try to
  try {
    // sign in anonymously to firebase
    await firebase.auth().signInAnonymously();
    // upload document
    const docRef = await collection.add(document);
    // output docRef to console
    console.log(docRef);
    // signout
    firebase.auth().signout();
  }
  // on error
  catch(error) {
    // output error to console
    const errorCode = error.code;
    const errorMessage = error.message;
    console.log(error,errorCode,errorMessage);
  };
};

最后,这是启动 web worker 的事件监听器:

// event handler
onmessage = async function(e) {
  // cache data
  const workerData = e.data;
  // upload data to firestore
  await uploadData(workerData);
  // send upload finished
  postMessage("upload finished");
};

Firestore 规则完全开放(仅用于测试目的):

rules_version = ''2'';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
     allow read,write;
    }
  }
}

但是,当我的网络工作者运行时,我在控制台中收到以下错误:

"permission-denied" "Missing or insufficient permissions"

我做错了什么?

解决方法

出于某种奇怪的原因,在当前的 firebase 项目中,配置对象缺少道具“databaseURL”。 我删除了该项目,创建了一个新项目,代码按预期工作。事实上,新的 firebase 配置对象正确设置了“databaseURL”属性。

我们今天的关于Web Workerwebworker在前端中的应用的分享已经告一段落,感谢您的关注,如果您想了解更多关于Apache Spark:“未能启动org.apache.spark.deploy.worker.Worker”或Master、Celery 4.4.0、django 和flower:Celery worker 接收到已经被其他 worker 执行的任务它没有执行但添加到处理中、ES6+Webpack 下使用 Web Worker、Firestore 拒绝使用完全开放的安全规则 (web-app) 在 web worker 中添加操作的相关信息,请在本站查询。

本文标签: