Javascript工作者-为什么工作者消息被处理得如此之晚,我能做些什么来反对它吗?

Javascript工作者-为什么工作者消息被处理得如此之晚,我能做些什么来反对它吗?,javascript,web-worker,sharedarraybuffer,Javascript,Web Worker,Sharedarraybuffer,我有一个与“主线程”共享SharedArrayBuffer的工作线程。为了正确工作,我必须确保工作线程在主线程访问SAB之前能够访问它。(编辑:创建辅助进程的代码必须在一个单独的函数中(编辑2:返回一个指向SAB的数组)。(也许,这已经不可能了,你会告诉我) 初始代码如下所示: function init() { var code = `onmessage = function(event) { console.log('starting'); var buffer=

我有一个与“主线程”共享SharedArrayBuffer的工作线程。为了正确工作,我必须确保工作线程在主线程访问SAB之前能够访问它。(编辑:创建辅助进程的代码必须在一个单独的函数中(编辑2:返回一个指向SAB的数组)。(也许,这已经不可能了,你会告诉我)

初始代码如下所示:

function init() {
  var code = `onmessage = function(event) {
      console.log('starting');
      var buffer=event.data;
      var arr = new Uint32Array(buffer);// I need to have this done before accessing the buffer again from the main
      //some other code, manipulating the array
  }`
  var buffer = new SharedArrayBuffer(BUFFER_ELEMENT_SIZE);
  var blob = new Blob([code], { "type": 'application/javascript' });
  var url = window.URL || window.webkitURL;
  var blobUrl = url.createObjectURL(blob);
  var counter = new Worker(blobUrl);
  counter.postMessage(buffer);
  let res = new Uint32Array(buffer);
  return res;
}

function test (){
  let array = init();
  console.log('main');
  //accessing the SAB again
};
工作代码总是在
test()
之后执行,控制台总是显示
main
,然后显示
启动

使用超时没有帮助。考虑以下代码:<代码>测试< /代码>:

function test (){
  let array = [];
  console.log('main'); 
  setTimeout(function(){
    array = initSAB();
  },0);
  setTimeout(function(){
    console.log('main');
   //accessing the SAB again
  },0);
  console.log('end');
};
控制台首先显示
end
,然后显示
main
,然后显示
start

但是,将缓冲区分配给test()函数外部的全局数组可以完成此任务,即使没有超时

我的问题如下:
  • 为什么在发送消息(=接收消息)后工作进程不直接启动。好了,工作人员有自己的事件队列,所以他们不应该依赖主堆栈变空
  • 是否有详细说明工作人员在发送消息后何时开始工作的规范
  • 是否有办法确保工作人员在再次访问SAB之前已启动,而不使用全局变量?(人们可能需要忙碌的等待,但我要小心……)可能没有办法,但我想确定
编辑 更准确地说:

  • 在完全并行运行的场景中,工作人员将能够 在邮件发布后立即处理该邮件。这显然是错误的 事实并非如此
  • 大多数浏览器API(Worker就是这样的API)都使用回调队列来处理对API的调用。但如果这一点适用,那么信息将是 在执行超时回调之前已过帐/已处理
  • 更进一步:如果我尝试在postMessage之后通过读取SAB直到它改变来忙着等待,一个值将阻止 无限编程。对我来说,这意味着浏览器可以 not在调用堆栈尽可能为空之前发布消息 我知道,这种行为没有记录,我无法解释
总结:如果postMessage调用在函数中,我想知道浏览器如何确定何时发布消息以及工作人员如何处理消息。我已经找到了一个解决方法(全局变量),因此我更感兴趣的是它在幕后的工作方式。但如果有人能给我看一个有效的例子,我就要了

编辑2: 使用全局变量(工作正常的代码)的代码如下所示

function init() {
//Unchanged
}

var array = init(); //global

function test (){
  console.log('main');
  //accessing the SAB again
};
它将
开始
,然后
打印到控制台

还有一点值得注意:如果我用Firefox浏览器调试代码(Chrome未测试)我得到了我想要的结果,而没有全局变量(
main
之前启动
),有人能解释一下吗?

根据MDN:

主页和辅助页之间传递的数据是复制的,而不是共享的。对象在传递给工作对象时被序列化,然后在另一端反序列化。页面和辅助对象不共享同一实例,因此最终结果是在每一端创建一个副本。大多数浏览器将此功能实现为结构化克隆

阅读更多关于

下面是一个与工作者共享缓冲区的基本代码。它创建了一个偶数值的数组
(i*2)
,并将其发送给工作者。它用于更改缓冲区值

要确保工作进程已启动,您可以使用不同的消息

var code=document.querySelector('[type=“javascript/worker”]').textContent;
var blob=new blob([code],{“type”:'application/javascript'});
var blobUrl=URL.createObjectURL(blob);
var计数器=新工人(blobUrl);
var-sab;
var initBuffer=函数(msg){
sab=新的SharedArrayBuffer(16);
反邮件({
init:是的,
味精:味精,,
缓冲区:sab
});
};
var editArray=函数(){
var res=新的Int32Array(sab);
for(设i=0;i<4;i++){
原子学.存储(res,i,i*2);
}
console.log('Array edited',res);
};
initBuffer('Init buffer and start worker');
counter.onmessage=函数(事件){
日志(event.data.msg);
if(event.data.edit){
editArray();
//与工作者共享新缓冲区
counter.postMessage({buffer:sab});
//终端工人
counter.postMessage({end:true});
}
};

var-sab;
self['onmessage']=函数(事件){
if(event.data.init){
postMessage({msg:event.data.msg,edit:true});
}
if(event.data.buffer){
sab=event.data.buffer;
var sharedArray=新的Int32Array(sab);
postMessage({msg:'共享数组:'+sharedArray});
}
if(event.data.end){
postMessage({msg:'Time to rest'});
}
};
为什么在sen[t](=接收到)消息后,工作进程不直接启动。另外,工作人员有自己的事件队列,因此他们不应该依赖主堆栈变空?

首先,即使您的工作线程对象在主线程中同步可用,但在实际的工作线程中,在能够处理您的消息之前,还需要执行以下操作:

  • 它必须检索脚本内容。即使使用blobURI,它也是一个异步操作
  • 它必须初始化整个js上下文,因此即使网络请求的速度非常快,这也会增加并行执行时间

  • 它必须在主脚本执行之后等待事件循环帧来处理消息。即使初始化速度极快,它也会等待一段时间
所以在正常情况下,您的工作人员执行代码的可能性很小