如何正确优化每次连接长字符串的长循环?

How to properly optimize a long for loop that concats a long string everytime?

我正在做一个小型学习项目,它从 API 中获取硬币列表,然后构建一个充满 bootstrap 卡片的页面,长度为从 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 个元素,你可以看到这个 link ->

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

长运行在JS中同步操作会锁死UI,.

为了防止这种情况,您可以将长 运行 sync 操作转换为 async 操作,这有利于 UI 一些呼吸 space.然后您可以创建加载指示器等。

下面是一个非常简单的示例,我正在执行一个大约需要 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 工作的最佳时间执行您的任务。

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

所以这是一个结合这两种方法的例子:https://codesandbox.io/s/7jz3yjow0q

基于此Google developer article

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