在Javascript中加载onload-event时没有页面渲染



我有一个香草Javascript设置img的src属性为data-uri。在分配给onload事件的函数中,我试图在页面上显示一些准备工作的进度,这可能需要一段时间(生成多个画布元素并设置一些设置)。

这是应该呈现进度条的函数:

let progress = document.querySelector('.progress');
function showProgress(message, percent) {
console.log(message + ' (' + (percent * 100).toFixed(0) + '%)');
progress.querySelector('.progress--message').innerHTML = message;
progress.querySelector('.progress--bar-container--bar').style.width = (percent * 100).toFixed(0) + '%';
}

代码如下所示:

img.onload = function() {
showProgress('Preparing image...' .1);
// Some preparations...
showProgress('Preparing tools...', .2);
// etc...
showProgress('Done...', 1);
}

控制台日志按预期工作,但是DOM元素的呈现直到函数结束才停止,并显示"完成";一切就绪后的状态

onload事件处理中有任何渲染阻塞吗?它可以被绕过吗?还是我错过了什么?代码本身对结果没有任何影响。甚至函数的第一行也有这种奇怪的行为。

除非您使用后台Web Workers消息(更复杂,特别是在您的情况下),否则呈现你的进度条只能在一个完整的返回之后发生。从你的剧本;因此,每次呼叫showProgress后。现在,如何返回,渲染然后重新启动剩余的? 那么,0毫秒的setTimeout就派上用场了。有了它,下面的代码就可以工作了:

img.onload = function() {
showProgress('Preparing image...' .1);
setTimeout(function() {
// Some preparations...
showProgress('Preparing tools...', .2);
setTimeout(function() {
// etc...
showProgress('Done...', 1);
}, 0);
}, 0);
}

嵌套代码的另一种方法是对一个步骤数组进行递归调用——但是每次调用都会在下面的调用开始之前完成,就像这样:

steps = [
function() { // first step (img loading just completed so, show Preparing it right away)
showProgress('Preparing image...', .1);
},
function() { // second step
// Some preparations...
showProgress('Preparing tools...', .2);
}, // other steps...
function() { // last step
// Last things to do...
showProgress('Done...', 1);
}
];
// function that processes "steps" - array of at least 1 function - until empty
function displayAfterEachStep() {
steps.shift()(); // Pull out the first step and run it
if(steps.length > 0) // if there's still steps
setTimeout(displayAfterEachStep, 0); // Recall the process after page rendering
}
img.onload = displayAfterEachStep;

在这里和周围可以找到许多其他的好主意来解决HTML中渲染事件的缺乏。

我在做了一些其他项目后回到了这个问题,并使用Promises解决了我的问题,像这样:

showProgress("Loading image...", 0.0) // Reset progress bar
const img = document.createElement("img") // Get image element
const promise = new Promise((resolve, reject) => { // Instantiate a Promise
img.onload = resolve // Set onload callback to resolve (this triggers the chain)
img.onerror = reject
img.src = "/path/to/image.jpg" // Set src attribute to start loading image
})
.then(() => { // When promise is resolved: Start step 2
showProgress("Preparing image...", 0.3)
// ...
})
.then(() => { // When promise is resolved: Start step 3
showProgress("Preparing tools...", 0.6)
// ...
})
.then(() => { // When promise is resolved: Finish up
showProgress("Ready...", 1)
// ...
})

使用Promises可以在保持可读性的同时将异步任务按顺序排列。

进一步阅读:MDN上的承诺

最新更新