【发布时间】:2021-03-01 14:13:48
【问题描述】:
我正在使用网络工作者来计算属于这些地方的坐标和值。计算完美地发生在后台,保持 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();
上面代码sn-p中的变量rehashedMap是一个带有数字键的对象。每个键包含一个array 和另一个object。
我的主线程(仅相关部分:)
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();
需要调用 end() 函数来计算自 webworker 启动以来的秒数。它返回全局设置的开始时间和调用时间之间的差异。
我的控制台中显示的内容:
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
问题: 我的 DOM 在数据传输开始和收到完整热图数据后的消息之间冻结。这是我的控制台中第一条消息和第二条消息之间的阶段。传输需要 16 秒,但 DOM 仅在其中一部分时间内无响应。 Webworkers 无法与主线程共享数据,因此需要传输。
问题:
首先,如何防止 DOM 在 webworker 的onmessage 阶段冻结?其次,更多的是出于好奇:这种冻结怎么可能只发生在该阶段的一部分,因为这些是由两个连续步骤触发的,中间没有任何事情发生?
到目前为止我尝试了什么:
- 对 rehashedMap 执行 for 循环并逐个键返回。这仍然会触发 DOM 冻结;更短,但不止一次。在极少数情况下,它会拉下标签。
- 寻找缓冲
onmessage阶段的方法;但是,与postMessage阶段 (https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) 相比,文档 (https://developer.mozilla.org/en-US/docs/Web/API/Worker/onmessage) 中没有指定这样的选项。我在这里遗漏了什么吗? - 作为测试,我将 rehashedMap 替换为一个空对象;这不会导致 DOM 冻结。当然,这让我无法访问计算数据。
- 我在 SO:Javascript WebWorker - Async/Await 上查看了这个帖子,但我不确定如何将该上下文与我的上下文进行比较。
【问题讨论】:
-
您在上面的几个地方使用了术语“JSON 对象”,我确定您只是指“对象”。 JSON 是一种用于数据交换的文本符号。 (More here.) 如果您正在处理 JavaScript 源代码,而不是处理 string,那么您就不是在处理 JSON。
-
我现在可以理解在解释问题时这是多么令人困惑。感谢您指出。我会在需要的地方进行更改。
-
你在不同的浏览器上体验过吗?你在这个数据中有循环引用吗?这个物体有多深?您的开发工具的性能选项卡说的是瓶颈。无论如何,如果你要从同一个脚本开始一个新的工作人员,不要杀死你的工作人员:启动一个工作人员对浏览器来说是一项巨大的工作,让它等待一个事件是什么。
-
请将您在以下答案中提供的信息作为edit 包含在您的问题中,并按照要求包含minimal reproducible example。另外,你真的需要在你的前端线程上拥有所有这些数据吗?我想你不能同时显示所有这些,所以有没有办法拆分这个请求,以便你的工作人员只生成真正需要的内容? (如果你能展示一些你的 visu 脚本也有帮助的话)。
标签: javascript asynchronous web-worker