如何正确地优化每次插入长字符串的long-for循环



我正在做一个小的学习项目,它从API获取一个硬币列表,然后为从API接收的数组的长度构建一个充满引导卡的页面。在开发过程中,我只使用了100个第一个硬币来避免漫长的等待时间,但现在我已经完成了,当我尝试整个3900多个硬币时,它需要不切实际的长时间。

我很确定我添加字符串的方式是问题的根源,我会添加我的代码,我相信这一切都会更有意义。

我试着构建整个字符串,然后附加它——速度仍然很慢。我试着每次都更改它的innerHTML,然后追加它(追加在for循环中),但它只是覆盖了所有旧的硬币,只追加最后一个。

我想让它做的是将每个盒子单独附加,并在合理的时间内完成,现在需要30多分钟才能完成,这显然不好。

我添加的当前版本的代码需要很长时间,但最终还是做对了(尽管for循环中只有50次迭代,所以如果你现在尝试它,它不会被卡住,它需要超过3900次迭代)

function homeStart(coins: any[]): void {
var divcreate = document.createElement("div");
for (var i = 0; i < 50; i++) {
// console.log(coins[i]);
// ******This is where the string addup starts
divcreate.innerHTML += `
<div class="card text-dark bg-dark m-auto makeinline" 
id="${coins[i].id}${i}" style="max-width: 18rem;">
<div class="card-header">
<div class="flexalign">
<span class="coinsymbol" 
id="${coins[i].symbol.toUpperCase() + 
"a1"}">${coins[i].symbol.toUpperCase()} 
</span>
<label class="switch">
<input type="checkbox" 
id="${coins[i].id}${coins[i].symbol}" 
onchange="selectedCoinUpdate(this,'${coins[i].symbol}')"> 
<span class="slider round"></span>
</label>
</div>
</div>
<div class="card-body">
<div class="">
<h5 class="card-title coinname">${coins[i].name}</h5>
<button type="button" class="btn btn-secondary" data- 
toggle="collapse" href="#collapseIdentity${i}" role="button"
aria-expanded="false" aria- 
controls="collapseIdentity${i}" 
onclick="moreInfo('${coins[i].id}')">More info</button>
</div>
<div class="collapse" id="collapseIdentity${i}">
<div class="card-body" id="${coins[i].id}">
</div>
</div>
</div>
</div>`;
}
// ******This is where the string addup ends
$("#pagecont").append(divcreate);
}

为了快速加载DOM元素,您可以看到这个链接->javascript中的内存泄漏,函数在循环中

在尽可能快地获取内容后,它可能仍然太慢,并可能导致浏览器冻结/崩溃。

JS中长时间运行的同步操作会锁定UI,。

为了防止这种情况,您可以将长时间运行的sync操作转换为async操作,这样做的好处是给UI一些喘息的空间。然后你可以创建一个装载指示器等

下面是一个非常简单的例子,我正在做一个大约100毫秒的同步操作,并做了100次。这总共需要10秒,如果你在不使用async的情况下这样做,那将不是一个很好的用户体验。

为了证明UI没有被锁定,我只是将Loading指示器闪烁为粉红色,当然你可以做一个旋转的圆圈等。完成后,它还会控制台日志all done,。。

希望这个小例子能对长期运行的sync操作有所帮助。

ps:我一直认为async/await是为了进行下一次勾选,对我来说,尽管在没有sleep(0)的情况下进行循环,仍然会导致UI锁定10秒。

const loaddiv = document.querySelector(".loading");
var i = setInterval(() => {
loaddiv.classList.toggle("loading");  
}, 300);
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
async function blockUI() {
let now = new Date().getTime();
const desiredTime = now + 100;
while (now < desiredTime) {
now = new Date().getTime(); 
}
await sleep(0); //give the UI some breathing space
}
async function longLoop() {
for (let l = 0; l < 100; l += 1) 
await blockUI();
clearInterval(i);  
console.log("all done");
}
longLoop();
.loading {
background-color: pink;
}
<div class="loading">
Loading.
</div>

正如Keith所说,您可以在多个任务中转换独特的同步操作,并以异步方式运行这些任务(在他的示例中为setTimeout)。

这样做而不是一个大的操作可以提高UI性能,但它并不是很好,因为你接触了DOM。

触摸DOM很慢,因为浏览器可能有很多操作要做(绘制、回流等)。因此,最好让浏览器在他接触DOM及其DOM工作的最佳时间完成任务。

为此,浏览器主要有两种方法可以使用:

  • requestAnimationFrame:通常用于执行动画,但现在是要求浏览器执行DOM操作的最佳时机
  • requestIdleCallback:执行后台和低优先级工作

下面是一个将这两种方法结合在一起的示例:https://codesandbox.io/s/7jz3yjow0q

它是基于这篇谷歌开发者的文章。

这里有一些关于网络性能的好资源:

  • https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/
  • https://developers.google.com/web/fundamentals/performance/rendering/
  • https://developers.google.com/web/fundamentals/performance/rail

相关内容

最新更新