Svelte中同步方法的加载指示器实现问题
Svelte中同步长任务的加载指示器解决方案
问题原因
JavaScript是单线程模型,同步的长时间任务会完全阻塞主线程。你之前的代码里,设置showLoading = true后立刻执行doIt(),主线程被同步任务占满,浏览器根本没时间更新DOM渲染加载状态;等doIt()执行完,showLoading又被立刻设为false,所以用户看不到任何加载提示。
用tick也无效的原因是:tick等待的是当前微任务队列清空后的DOM更新,但await tick()之后的doIt()还是同步阻塞主线程,浏览器依然没有机会渲染加载状态。
解决方案
方案1:用setTimeout将任务放入宏任务队列
通过setTimeout把同步任务延迟到下一个宏任务执行,给浏览器留出渲染UI的时间:
<script> import {doIt} from "./thingDoer.js" let showLoading = false; async function doTheThing() { showLoading = true; // 将同步任务放入宏任务队列,让浏览器先完成UI渲染 await new Promise(resolve => { setTimeout(() => { doIt(); resolve(); }, 0); }); showLoading = false; } </script> {#if showLoading} <h1>Loading...</h1> {:else} <button on:click={doTheThing}>Do the Thing</button> {/if}
原理:setTimeout(..., 0)会把任务推到宏任务队列,await这个Promise时,主线程会先处理完当前的DOM更新(显示Loading),再执行doIt(),任务完成后再更新showLoading状态。
方案2:使用Web Worker(推荐用于重度耗时任务)
如果doIt()是真正的重型计算任务,推荐用Web Worker在后台线程执行,彻底避免阻塞主线程:
步骤1:创建Worker文件(thingDoer.worker.js)
import {doIt} from "./thingDoer.js"; // 监听主线程消息 self.onmessage = (event) => { // 执行耗时任务 doIt(); // 任务完成后通知主线程 self.postMessage("task-completed"); };
步骤2:在组件中使用Worker
<script> let showLoading = false; async function doTheThing() { showLoading = true; // 创建Worker实例 const worker = new Worker("./thingDoer.worker.js"); // 向Worker发送启动信号 worker.postMessage("start-task"); // 等待Worker完成任务 await new Promise(resolve => { worker.onmessage = (event) => { if (event.data === "task-completed") { // 终止Worker释放资源 worker.terminate(); resolve(); } }; }); showLoading = false; } </script> {#if showLoading} <h1>Loading...</h1> {:else} <button on:click={doTheThing}>Do the Thing</button> {/if}
原理:Web Worker运行在独立的后台线程,不会占用主线程资源,所以设置showLoading = true后,浏览器能立刻渲染加载状态,任务完成后再通过消息通知主线程更新状态。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

