使用document.getElementById()时循环操作异常,问题出在哪里?
问题:表格行无法逐行延迟显示,仅在循环结束后一次性出现
脚本任务
- 生成维度由表单数据决定的数字数组,由
send_n()函数负责; - 在页面显示两列表格:数组元素与当前数组正元素的累加和;
- 在表格末尾用一行两列输出正元素的最终总和;
- 表格每行需延迟1秒出现,自定义了
timeout(ms)函数实现,认为setTimeout异步会导致calculation_x函数异常。
问题现象
触发循环时控制台输出符合预期,但表格仅在循环结束后才一次性显示所有行。
尝试过的方法
曾使用setTimeout()和setInterval(),但未得到理想结果。
代码
JavaScript
function timeout(ms) { let date = new Date(); while ((new Date()) - date <= ms) { // w/o do } } function calculation_x(arr, count) { let i = 0; let sum = 0; let table = String(`<table border="1px"><thead><tr><td>Элемент массива</td><td>Сумма положительных элементов массива</td></tr></thead><tbody id="result"></tbody></table>`); document.getElementById(`table-div`).innerHTML = table; for (i = 0; i < count; i++) { if (arr[i] > 0) { sum += arr[i]; } table = `<tr><td>X[${i}] = ${arr[i]}</td><td>${sum}</td></tr>`; document.getElementById(`result`).innerHTML += table; console.log(`X[${i}] = ${arr[i]} | ${sum}`); timeout(1000); } timeout(1000); let result = `<tr><td colspan="2">Результат: ${sum}</td></tr>`; document.getElementById(`result`).innerHTML += result; console.log(`Сумма положительных элементов: ${sum}`); } function send_n() { let i = 0; let n = Number(document.getElementById(`n`).value); let numbers = new Array(n); for (i = 0; i < n; i++) { numbers[i] = Number(Math.floor(Math.random() * 200) - 100); } calculation_x(numbers, n); }
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form> <input type="text" id="n" placeholder="Количество элементов массива"> <input type="button" value="Принять" onclick="send_n()"> </form> <div id="table-div"></div> <script src="/scripts.js"></script> </body> </html>
问题原因
你自定义的timeout函数是同步阻塞实现,通过while循环持续占用JS主线程。而浏览器的UI渲染与JS执行共享同一主线程,同步阻塞会导致所有DOM更新操作被积压,直到整个循环结束后才会一次性渲染,因此表格行无法逐行显示。
解决方案
使用Promise封装的异步延迟函数配合async/await,既不会阻塞主线程,又能保证每行按顺序延迟1秒出现。修改后的代码如下:
修改后的JavaScript代码
// 改用Promise封装的异步延迟函数 function timeout(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 将calculation_x改为async函数,使用await实现顺序延迟 async function calculation_x(arr, count) { let sum = 0; const table = `<table border="1px"><thead><tr><td>数组元素</td><td>正元素累加和</td></tr></thead><tbody id="result"></tbody></table>`; document.getElementById(`table-div`).innerHTML = table; const resultTbody = document.getElementById(`result`); for (let i = 0; i < count; i++) { if (arr[i] > 0) { sum += arr[i]; } const row = `<tr><td>X[${i}] = ${arr[i]}</td><td>${sum}</td></tr>`; resultTbody.innerHTML += row; console.log(`X[${i}] = ${arr[i]} | ${sum}`); // 等待1秒,此时浏览器可正常渲染DOM await timeout(1000); } await timeout(1000); const finalRow = `<tr><td colspan="2">最终结果: ${sum}</td></tr>`; resultTbody.innerHTML += finalRow; console.log(`正元素总和: ${sum}`); } function send_n() { const n = Number(document.getElementById(`n`).value); const numbers = Array.from({ length: n }, () => Math.floor(Math.random() * 200) - 100); calculation_x(numbers, n); }
修改说明
- 异步延迟函数:将同步阻塞的
timeout改为返回Promise的异步函数,配合await实现非阻塞延迟。 - async函数标记:给
calculation_x添加async标记,允许在循环内使用await,保证每行渲染后等待1秒再执行下一步。 - DOM缓存:提前获取
resulttbody元素,避免循环内重复查询DOM,提升性能。 - 简化数组生成:用
Array.from简化随机数组生成逻辑,功能不变。
修改后,表格行会每隔1秒逐行渲染到页面,控制台输出同步显示,最终总和行也会在最后一行显示1秒后出现。
内容的提问来源于stack exchange,提问作者Ivanov Vladislav
相关产品推荐
相关产品推荐

