如何让await函数延迟小于1毫秒?排序可视化工具优化需求
解决排序可视化工具高速度(每秒千次以上比较)的问题
问题根源
浏览器的setTimeout存在最小延迟限制(通常约4ms),即使设置delay为0或1ms,实际等待时间也会被浏览器强制拉高,导致无法达到每秒1000次以上的比较速度。此外,每步单独更新DOM的操作也会带来额外性能开销,进一步拖慢执行效率。
解决方案:批量处理+帧同步
当delay处于0-1ms区间时,跳过逐步骤的setTimeout,改为在每一帧(通过requestAnimationFrame)内批量执行多个排序步骤,同时批量更新DOM。这种方式既能突破浏览器的延迟限制,又能保证可视化的流畅性,轻松实现每秒千次以上的比较速度。
修改后的完整代码实现
HTML(无改动)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sorting Algorithm Visualizer</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="controls"> <select id="algorithm"> <option value="bubble">Bubble Sort</option> <option value="selection">Selection Sort</option> <option value="insertion">Insertion Sort</option> </select> <input type="text" id="Size" value=100> <button onclick="gen()">Generate</button> <input type="number" id="delay" min="0" value="1"> (ms delay) </div> <div class="bars" id="setup"></div> <script src="script.js"></script> </body> </html>
CSS(无改动)
.bars{ background-color: red; width: 10px; height: 10px; position: absolute; bottom: 100%; margin: 0px; visibility: hidden; }
JavaScript(核心修改)
var list; var ind; var size; var sort; var delay; var count; var height = Math.floor(window.innerHeight * 0.9); var width = Math.floor(window.innerWidth); // 批量处理步长,可根据设备性能调整 const BATCH_SIZE = 50; function swap(arr, a, b) { [arr[a], arr[b]] = [arr[b], arr[a]]; } // 批量更新DOM元素,减少重复操作 function batchUpdate(indices) { indices.forEach(item => { const bar = document.getElementById("bar" + item); if (bar) { bar.style.visibility = "visible"; bar.style.height = (height / size) * list[item] + "px"; const hue = (list[item] / size) * 360; bar.style.backgroundColor = `hsl(${hue}, 100%, 50%)`; } }); } // 自定义等待函数,根据delay值选择不同策略 function sleep(ms) { if (ms <= 1) { // 低延迟时,与浏览器渲染帧同步 return new Promise(resolve => requestAnimationFrame(resolve)); } else { return new Promise(resolve => setTimeout(resolve, ms)); } } async function bubbleSort(arr) { let updatedIndices = new Set(); for (var i = 0; i < arr.length; i++) { for (var j = 0; j < (arr.length - i - 1); j++) { if (arr[j] > arr[j + 1]) { swap(list, j, j + 1); updatedIndices.add(j); updatedIndices.add(j + 1); } // 低延迟下批量处理+帧同步 if (delay <= 1 && (j % BATCH_SIZE === 0 || j === arr.length - i - 2)) { batchUpdate(Array.from(updatedIndices)); updatedIndices.clear(); await sleep(delay); } else if (delay > 1) { // 正常延迟下逐步骤更新 batchUpdate([j, j+1]); await sleep(delay); } } } // 最终更新所有元素确保状态正确 batchUpdate(Array.from({length: arr.length}, (_,k) => k)); } async function selectionSort(arr) { let updatedIndices = new Set(); for (let i = 0; i < arr.length; i++) { let lowest = i; for (let j = i + 1; j < arr.length; j++) { if (arr[j] < arr[lowest]) { lowest = j; updatedIndices.add(i); updatedIndices.add(lowest); } if (delay <= 1 && (j % BATCH_SIZE === 0 || j === arr.length - 1)) { batchUpdate(Array.from(updatedIndices)); updatedIndices.clear(); await sleep(delay); } else if (delay > 1) { batchUpdate([j, lowest]); await sleep(delay); } } if (lowest !== i) { swap(list, i, lowest); updatedIndices.add(i); updatedIndices.add(lowest); } if (delay <=1) { batchUpdate(Array.from(updatedIndices)); updatedIndices.clear(); await sleep(delay); } else { batchUpdate([i, lowest]); await sleep(delay); } } batchUpdate(Array.from({length: arr.length}, (_,k) => k)); } function shuffle(arr) { ind = list.length - 1; for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); swap(arr, i, j); } } function createBars(arr) { count = 0; while (count < arr.length) { var bar = document.createElement('div'); bar.classList.add("bars"); bar.setAttribute('id', "bar" + count); document.body.appendChild(bar); bar.style.width = Math.ceil(width / size) + "px"; bar.style.visibility = "visible"; bar.style.bottom = "0%"; bar.style.left = count * width / size + "px"; bar.style.height = (height / size) * arr[count] + "px"; const hue = (arr[count] / size) * 360; bar.style.backgroundColor = `hsl(${hue}, 100%, 50%)`; count++; } } async function gen() { if (list != undefined) { // 移除旧的bar元素 document.querySelectorAll('.bars').forEach(bar => bar.remove()); } list = []; size = parseInt(document.getElementById("Size").value); for (count = 1; count <= size; count++) { list.push(count); } shuffle(list); createBars(list); await sleep(1000); sort = document.getElementById("algorithm").value; delay = parseInt(document.getElementById("delay").value); switch (sort) { case "bubble": await bubbleSort(list); break; case "selection": await selectionSort(list); break; } } function updateAll() { batchUpdate(Array.from({length: list.length}, (_,k) => k)); }
关键优化说明
- 批量处理:低延迟模式下,每执行BATCH_SIZE次排序步骤后再更新DOM,减少DOM操作次数
- 帧同步:使用
requestAnimationFrame替代setTimeout,确保更新与浏览器渲染节奏一致,避免阻塞主线程 - 修复原代码问题:移除无效的
parent元素引用,将delay转为数字类型,避免类型错误 - 状态跟踪:使用
Set记录需要更新的元素索引,避免重复操作
内容的提问来源于stack exchange,提问作者BATTLEBOTS_BOI
相关产品推荐
相关产品推荐

