JavaScript中长任务执行时加载指示器无法持续显示的问题
解决长耗时任务中加载指示器不显示的问题
问题根源
JavaScript是单线程模型,浏览器的JS执行线程与渲染线程互斥。你的代码中,调用showLoader()后立刻执行了同步的长循环任务,主线程被占满,浏览器根本没时间更新DOM(渲染加载指示器),等循环结束后又马上调用hideLoader(),所以加载指示器只会短暂存在,肉眼完全看不到。
解决方案1:用setTimeout让出渲染时间
把长任务放到下一个事件循环队列,让浏览器先完成DOM渲染(显示加载指示器),再执行耗时任务:
<button id="test"> Do Something </button> <div id="loader" class="loader"> Loading... </div>
const button = document.getElementById("test"); const loader = document.getElementById("loader"); button.addEventListener("click", (_) => { showLoader(); // 将长任务延迟到下一个事件循环,给浏览器留渲染时间 setTimeout(() => { for (let i = 0; i < 500000; i++) { console.log("do the work"); } hideLoader(); }, 0); }) function showLoader() { loader.classList.add("loader--show"); } function hideLoader() { loader.classList.remove("loader--show"); }
.loader { display: none; &--show { display: block; } }
解决方案2:用Web Worker处理重型任务(推荐)
如果任务确实非常耗时,setTimeout仍会导致页面卡顿,此时可以用Web Worker将任务放到后台线程执行,完全不阻塞主线程:
- 新建
worker.js文件:
self.onmessage = function(e) { // 在后台线程执行长任务 for (let i = 0; i < 500000; i++) { console.log("do the work"); } // 任务完成后通知主线程 self.postMessage("done"); };
- 主线程代码:
<button id="test"> Do Something </button> <div id="loader" class="loader"> Loading... </div>
const button = document.getElementById("test"); const loader = document.getElementById("loader"); button.addEventListener("click", (_) => { showLoader(); // 创建Web Worker实例 const worker = new Worker("worker.js"); // 发送消息启动任务 worker.postMessage("start"); // 监听任务完成事件 worker.onmessage = function(e) { hideLoader(); // 关闭Worker释放资源 worker.terminate(); }; }) function showLoader() { loader.classList.add("loader--show"); } function hideLoader() { loader.classList.remove("loader--show"); }
.loader { display: none; &--show { display: block; } }
说明
- 方案1适合轻量级的长任务,实现简单;
- 方案2适合真正的重型计算,能保证页面全程流畅可交互。
内容的提问来源于stack exchange,提问作者ckinte
相关产品推荐
相关产品推荐

