JavaScript结合SigmaJS实现算法可视化时UI刷新仅在函数末尾执行
问题根因
浏览器采用单线程事件循环模型运行JavaScript,界面重绘、DOM更新这类渲染任务,必须等当前同步调用栈完全清空、主线程空闲时才会执行。你的代码刚好卡在这个机制上:
- 你写的
sleep是忙等实现:靠while循环空转占满CPU,执行期间主线程完全被占死,浏览器根本没机会处理渲染任务 - 整个算法流程全是同步代码:从点击按钮触发
Dijkstra()开始,到算法遍历完所有节点结束,全程占着主线程。你调用的s.refresh()、createQueue()虽然已经修改了内存里的图数据、更新了DOM节点内容,但浏览器腾不出手把这些变更绘制到屏幕上,所有更新都会被攒到算法完全跑完后一次性渲染,自然看不到中间执行步骤。
断点调试时显示正常也很好解释:断点会强制挂起JS执行,把主线程交还给浏览器,浏览器就能趁这个间隙完成中间状态的渲染。
修复步骤
核心思路是把同步阻塞的算法逻辑改成异步非阻塞模式,每执行完一步就把主线程还给浏览器完成渲染,等渲染、延迟结束后再跑下一步。
- 替换错误的忙等sleep
把原来空转占线程的sleep换成基于setTimeout的异步实现,不要阻塞主线程:sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } - 改造算法相关方法为异步函数
所有涉及算法执行、需要等待延迟的方法都加async关键字,调用异步方法、sleep时前面加await:
首先改启动入口:
再改造Dijkstra和遍历方法,参考你现有代码的适配示例:async function onStartClick(){ if(radio === 'p') await graphe.Prim(); if(radio === 'd') await graphe.Dijkstra(0); }async Dijkstra(start){ this.tabVisited = []; this.tabRencontred = []; for(var i = 0; i < Object.keys(this.adjacencyList).length; i++){ this.tabVisited.push(false); this.tabRencontred.push(false); } await this.visiterPriorite(start); } async visiterPriorite(sommetCourant) { if(!this.tabVisited[sommetCourant]) { var priorityQueue = new PriorityQueue(); priorityQueue.enqueue(sommetCourant, 0); var csChar = Object.keys(this.adjacencyList)[sommetCourant]; s.graph.nodes().forEach(n => { if(n.id === csChar) { n.color = "grey"; } }); s.refresh(); createQueue(priorityQueue.items) // 等待指定延迟,期间主线程空闲,浏览器可以完成当前状态渲染 await this.sleep(500); while(!priorityQueue.isEmpty()) { // 此处保留你原有的出队、遍历邻接节点、更新距离的逻辑 // 注意:每次修改节点状态、更新优先队列、调用s.refresh()之后 // 都要加await this.sleep(单步停留时长),保证每一步都能被渲染 } } }
注意事项
- 所有调用
sleep的位置必须加await,否则异步sleep不会按预期等待 - Prim算法也按照同样的逻辑改造为异步即可
- 绝对不要用while空转的方式实现延迟,这种写法会卡死整个页面,不光动画出不来,还会导致页面无响应。
内容的提问来源于stack exchange,提问作者michjea
相关产品推荐
相关产品推荐

