如何避免短耗时计算时加载图标闪烁?优化DOM延迟显示方案
如何避免异步计算短时延迟时加载图标的闪烁问题
我开发的脚本需要执行耗时不定的计算,长耗时计算时要显示"腕表"加载图标告知用户,但短耗时计算时不想让图标闪烁。尝试过setTimeout()延迟显示图标,但由于异步计算可能在定时器触发前完成,导致图标仍会短暂闪烁;用Web Worker异步执行计算后问题依旧;目前用fadeIn()能解决但不够优雅,求更合适的实现方法。
最优方案:定时器+取消机制
核心思路是:设置一个延迟显示图标的定时器,同时在计算完成时立即取消该定时器——如果计算耗时短(小于延迟时间),定时器还未触发就被取消,图标不会显示;如果计算耗时长,定时器到点触发,图标正常显示。
修改你的main.html代码如下:
<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script> jQuery(function($){ const worker = new Worker("asyncWorker.js"); let waitTimer = null; // 保存定时器ID $("#shortbtn,#longbtn").click(function(){ $("#done").hide(); $("#wait").hide(); // 确保初始状态隐藏 // 设置延迟显示图标的定时器 waitTimer = setTimeout(function(){ $("#wait").show(); }, 100); let btnId = $(this).attr("id"); worker.postMessage(btnId); // 计算完成后的处理:取消定时器+更新状态 function handleComplete() { clearTimeout(waitTimer); // 取消未触发的定时器 $("#wait").hide(); $("#done").show(); worker.removeEventListener("message", handleComplete); // 移除监听避免重复触发 } worker.addEventListener("message", handleComplete); }); }) </script> <style> #wait,#done{display:none;} #wait{font-size: 4em;} </style> </head> <body> <button id="shortbtn">Short delay</button> <button id="longbtn">Long delay</button> <div id="div"> <span id="wait">⌚</span> <span id="done">Done!</span> </div> </body> </html>
方案说明
- 点击按钮时,初始化定时器,延迟100ms显示加载图标
- 计算完成(Worker返回消息)时,立即调用
clearTimeout()取消定时器:- 若计算耗时<100ms:定时器未触发就被取消,图标永远不会显示,彻底避免闪烁
- 若计算耗时>100ms:定时器已触发,图标正常显示,此时隐藏图标并切换到完成状态
- 每次处理完成后移除
message事件监听,避免多次点击导致的重复绑定问题
其他可选方案:Promise竞争
用Promise实现"延迟显示"和"计算完成"的竞争,逻辑更清晰,适合现代JS写法:
<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script> jQuery(function($){ const worker = new Worker("asyncWorker.js"); $("#shortbtn,#longbtn").click(function(){ $("#done").hide(); $("#wait").hide(); const btnId = $(this).attr("id"); // 创建计算完成的Promise const computePromise = new Promise(resolve => { function handleMessage(msg) { resolve(); worker.removeEventListener("message", handleMessage); } worker.addEventListener("message", handleMessage); worker.postMessage(btnId); }); // 创建延迟显示的Promise const delayPromise = new Promise(resolve => { setTimeout(() => { $("#wait").show(); resolve(); }, 100); }); // 不管哪个Promise先完成,最终都要隐藏图标显示完成状态 Promise.race([computePromise, delayPromise]).then(() => { computePromise.then(() => { $("#wait").hide(); $("#done").show(); }); }); }); }) </script> <style> #wait,#done{display:none;} #wait{font-size: 4em;} </style> </head> <body> <button id="shortbtn">Short delay</button> <button id="longbtn">Long delay</button> <div id="div"> <span id="wait">⌚</span> <span id="done">Done!</span> </div> </body> </html>
方案说明
Promise.race()会在第一个Promise完成时触发回调- 若计算先完成(短耗时):延迟Promise还没触发,图标不会显示,直接进入完成状态
- 若延迟先完成(长耗时):图标显示,之后计算完成时隐藏图标并显示完成状态
内容的提问来源于stack exchange,提问作者danbae
相关产品推荐
相关产品推荐

