Javascript 如何使webworker的onmessage阶段异步?

Javascript 如何使webworker的onmessage阶段异步?,javascript,asynchronous,web-worker,Javascript,Asynchronous,Web Worker,我正在使用webworker计算属于这些位置的坐标和值。计算完全在后台进行,保持DOM的响应性。但是,当我将数据从webworker发送回主线程时,DOM会在一部分传输时间内没有响应 我的网络工作者(发送部分): //calculates happen before; this is the final step to give the calculated data back to the mainthread. var processProgressGEO = {'cmd':'geoRepo

我正在使用webworker计算属于这些位置的坐标和值。计算完全在后台进行,保持DOM的响应性。但是,当我将数据从webworker发送回主线程时,DOM会在一部分传输时间内没有响应

我的网络工作者(发送部分):

//calculates happen before; this is the final step to give the calculated data back to the mainthread.
var processProgressGEO = {'cmd':'geoReport', 'name': 'starting transfer to main', 'current': c, 'total': polys}
postMessage(processProgressGEO);
postMessage({
  'cmd':'heatmapCompleted',
  'heatdata': rehashedMap,
  'heatdatacount': p,
  'current': c,
  'total': polys,
  'heatmapPeak': peakHM,
});
self.close();
var heatMaxforAuto = 1000000;  //maximum amount of datapoints allowed in the texdata. This takes into account the spread of a singel datapoint.
async function fetchHeatData(){
  return new Promise((resolve, reject) => {
    var numbercruncher = new Worker('calculator.js');
    console.log("Performing Second XHR request:");
    var url2 = 'backend.php?datarequest=geodata'
    $.ajax({
      type: "GET",
      url: url2,
    }).then(async function(RAWGEOdata) {
      data.georaw = RAWGEOdata;
      numbercruncher.onmessage = async function(e){
        var w = (e.data.current/e.data.total)*100+'%';
        if (e.data.cmd === 'geoReport'){
          console.log("HEAT: ", e.data.name, end(),'Sec.' );
        }else if (e.data.cmd === 'heatmapCompleted') {
          console.log("received Full heatmap data: "+end());
          data.heatmap = e.data.heatdata;
          console.log("heatData transfered", end());
          data.heatmapMaxValue = e.data.heatmapPeak;
          data.pointsInHeatmap = e.data.heatdatacount;
          console.log("killing worker");
          numbercruncher.terminate();
          resolve(1);
        }else{
          throw "Unexpected command received by worker: "+ e.data.cmd;
        }
      }
      console.log('send to worker')
      numbercruncher.postMessage({'mode':'geo', 'data':data});
    }).catch(function(error) {
      reject(0);
      throw error;
    })
  });
}

async function makemap(){
  let heatDone = false;
      if (data.texdatapoints<= heatMaxforAuto){
      heatDone = await fetchHeatData();
    }else{
      var manualHeatMapFetcher = document.createElement("BUTTON");
      var manualHeatMapFetcherText = document.createTextNode('Fetch records');
      manualHeatMapFetcher.appendChild(manualHeatMapFetcherText);
      manualHeatMapFetcher.id='manualHeatTriggerButton';
      manualHeatMapFetcher.addEventListener("click", async function(){
        $(this).toggleClass('hidden');
        heatDone = await fetchHeatData();
        console.log(heatDone, 'allIsDone', end());
      });
      document.getElementById("toggleIDheatmap").appendChild(manualHeatMapFetcher);
    }


}

makemap();
HEAT:  starting transfer to main 35 Sec.   animator.js:44:19
received Full heatmap data: 51             animator.js:47:19
heatData transfered 51                     animator.js:49:19
killing worker                             animator.js:52:19

1 allIsDone 51
上面代码段中的变量
rehashedMap
是一个带有数字键的对象。每个键都包含一个
数组
,其中包含另一个
对象

我的主线程(仅相关部分:)

问题: 我的DOM在数据传输开始和收到完整热图数据后的消息之间冻结。这是我的控制台中第一条和第二条消息之间的阶段。传输需要16秒,但DOM在这段时间内只有一次没有响应。Webworkers无法与主线程共享数据,因此需要进行传输

问题: 首先,如何防止在webworker的
onmessage
阶段冻结DOM?第二,更出于好奇:这种冻结怎么可能只发生在该阶段的一部分,因为它们是由两个连续步骤触发的,而中间没有发生任何事情

到目前为止我所尝试的:

//calculates happen before; this is the final step to give the calculated data back to the mainthread.
var processProgressGEO = {'cmd':'geoReport', 'name': 'starting transfer to main', 'current': c, 'total': polys}
postMessage(processProgressGEO);
postMessage({
  'cmd':'heatmapCompleted',
  'heatdata': rehashedMap,
  'heatdatacount': p,
  'current': c,
  'total': polys,
  'heatmapPeak': peakHM,
});
self.close();
var heatMaxforAuto = 1000000;  //maximum amount of datapoints allowed in the texdata. This takes into account the spread of a singel datapoint.
async function fetchHeatData(){
  return new Promise((resolve, reject) => {
    var numbercruncher = new Worker('calculator.js');
    console.log("Performing Second XHR request:");
    var url2 = 'backend.php?datarequest=geodata'
    $.ajax({
      type: "GET",
      url: url2,
    }).then(async function(RAWGEOdata) {
      data.georaw = RAWGEOdata;
      numbercruncher.onmessage = async function(e){
        var w = (e.data.current/e.data.total)*100+'%';
        if (e.data.cmd === 'geoReport'){
          console.log("HEAT: ", e.data.name, end(),'Sec.' );
        }else if (e.data.cmd === 'heatmapCompleted') {
          console.log("received Full heatmap data: "+end());
          data.heatmap = e.data.heatdata;
          console.log("heatData transfered", end());
          data.heatmapMaxValue = e.data.heatmapPeak;
          data.pointsInHeatmap = e.data.heatdatacount;
          console.log("killing worker");
          numbercruncher.terminate();
          resolve(1);
        }else{
          throw "Unexpected command received by worker: "+ e.data.cmd;
        }
      }
      console.log('send to worker')
      numbercruncher.postMessage({'mode':'geo', 'data':data});
    }).catch(function(error) {
      reject(0);
      throw error;
    })
  });
}

async function makemap(){
  let heatDone = false;
      if (data.texdatapoints<= heatMaxforAuto){
      heatDone = await fetchHeatData();
    }else{
      var manualHeatMapFetcher = document.createElement("BUTTON");
      var manualHeatMapFetcherText = document.createTextNode('Fetch records');
      manualHeatMapFetcher.appendChild(manualHeatMapFetcherText);
      manualHeatMapFetcher.id='manualHeatTriggerButton';
      manualHeatMapFetcher.addEventListener("click", async function(){
        $(this).toggleClass('hidden');
        heatDone = await fetchHeatData();
        console.log(heatDone, 'allIsDone', end());
      });
      document.getElementById("toggleIDheatmap").appendChild(manualHeatMapFetcher);
    }


}

makemap();
HEAT:  starting transfer to main 35 Sec.   animator.js:44:19
received Full heatmap data: 51             animator.js:47:19
heatData transfered 51                     animator.js:49:19
killing worker                             animator.js:52:19

1 allIsDone 51
  • 在rehashedMap上执行for循环,并逐键返回。这仍然会触发DOM冻结;更短,但不止一次。在极少数情况下,它会将选项卡取下
  • 寻找缓冲
    onmessage
    阶段的方法;但是,与
    postMessage
    阶段()相比,文档()中没有指定此类选项。我是不是遗漏了什么
  • 作为测试,我用一个空对象替换了重新绘制的地图;这并没有在DOM中造成任何冻结。当然,这让我无法访问计算数据
  • 我是这样看待这条线索的:但我不确定如何将这条线索与我的进行比较
  • 选择权 你应该把这和网络工作者联系起来,这是可以理解的,但这可能与此无关。我错了,是的。我认为问题可能有两个原因:

  • (我们知道这对于OP是不正确的,但对于其他人来说可能仍然相关。)问题可能是,一旦收到热图,您就有很多DOM操作要做。如果在不让主线程执行任何其他操作的紧密循环中执行此操作,那么在此期间页面将没有响应

    如果是这样的话,您必须找到一种更快速地进行DOM操作的方法(有时是可能的,有时不是),或者找到一种将其分割成块并分别处理每个块的方法,在块之间返回浏览器,以便浏览器可以处理任何挂起的UI工作(包括渲染新元素)

    您还没有将正在使用热图进行的DOM工作包括在内,因此实际上不可能为您提供解决问题的代码,但是“分割”将通过处理数据的子集,然后使用
    setTimeout(fn,0)
    (可能与
    requestAnimationFrame
    结合使用,以确保重新绘制已发生)在短暂地向浏览器屈服后安排继续工作(使用
    fn

  • 如果这确实是在辅助线程和主线程之间传输数据所花费的时间,则您可能可以将a用于热图数据,而不是当前对象,尽管这样做可能需要显著更改您的数据结构。使用可转移对象,您可以避免将数据从辅助线程复制到t主线程;相反,工作线程将实际内存转移到主线程(工作线程失去对可转移对象的访问权,而主线程获得了对该对象的访问权——所有这些都不需要复制)。例如,类型化数组(
    Int32Array
    )使用的内存是可转移的

  • 如果这真的是从工作者那里接收数据所花费的时间(从你的实验中听起来似乎是这样),那么使用可转移数据是不可取的(例如,因为你需要数据的格式与可转移数据不兼容),我所能看到的唯一剩下的选择是让工作线程发送主脚本较小的数据块,这些数据块的间距足以使主线程保持响应(甚至可能在数据可用时发送数据)

  • 仔细看#3 您描述了一个包含1600个条目的数组,其中每个条目都是一个包含0到“远远超过7000”个对象的数组,每个对象都有三个属性(带数值)。这意味着超过560万个对象。克隆这些数据需要相当长的时间,这并不奇怪

    下面是您描述的问题的一个示例:

    const workerCode=document.getElementById(“worker”).textContent;
    constworkerblob=newblob([workerCode],{type:“text/javascript”});
    常量workerUrl=(window.webkitURL | | window.URL).createObjectURL(workerBlob);
    const worker=新的worker(workerUrl);
    worker.addEventListener(“消息”,({data})=>{
    if((数据和数据操作)=“数据”){
    log(Date.now(),`Received${data.array.length}rows`);
    如果(data.done){
    停止旋转();
    }
    }
    });
    document.getElementById(“btn go”).addEventListener(“单击”,()=>{
    log(Date.now(),“请求数据”);
    startSpinning();
    worker.postMessage({action:“go”});
    });
    常量微调器=document.getElementById(“微调器”);
    常数状态=[…”▁▂▃▄▅▆▇█▇▆▅▄▃▂▁"];
    设stateIndex=0;
    设spinHandle=0;
    设maxDelay=0;
    设intervalStart=0;
    函数startSpinning(){
    if(微调器){
    取消动画帧(spinHandle);
    maxDelay=0;
    队列更新();
    }
    }
    函数queueUpdate(){
    间隔星