HTML 中承诺循环的记录进度不起作用

Logging progress of loop of promises in HTML is not working

使用下面的代码,我试图记录一系列循环的进度。

但是,如您所见,DOM 中的进度并未顺利记录。许多百分比增量被跳过。

我该如何解决?

;(async () => {
  let width = document.getElementById('width')
  let numOfImages = 8
  let images = Array.from(Array(numOfImages).keys())
  let pixelsPerImage = 3600
  let totalPixels = numOfImages * pixelsPerImage
  let currentLoop = 0

  for (let img of images) {
    await new Promise((resolve) => {
      // this setTimeout is actually an image.onload callback
      setTimeout(() => {
        let i = 0
        while (i < pixelsPerImage) {
          ;(function(cloop) {
            window.requestAnimationFrame(() => {
              let prcnt = ((100/totalPixels) * cloop).toFixed(2)
              width.innerHTML = prcnt + '%'
              width.style.width = prcnt + '%'
            })
          })(++currentLoop)
          i++
        }
        resolve()
      }, 5000)
    })
  }

  await Promise.all(promises)
})()

https://jsfiddle.net/koyfa6pm/1/

看起来您正在尝试 运行 8 个并行承诺,但它们 运行 一个接一个。

它是这样的: 创建了 8 个 promise,一个接一个。每个 promise 都会注册一个 5000 毫秒的 setTimeout。

一旦 5000 毫秒结束,将有 8 个回调等待,运行时间将一个接一个地调用它们(JavaScript 是单线程)。

第一个 promise 将执行 3600 次循环,并将写入 console.log 3600 次。然后下一个 promise 回调将在 运行 时间调用,下一个 3600 循环将做同样的事情...直到所有 8 个循环都结束。

你看到很多 console.log 因为你做了 3600*8 次...你没有看到 DOM 变化,因为浏览器只会在执行完成后呈现。

为了看到 dom 变化,您必须“释放”代码的执行,您可以使用 setTimeout(..., 0) 执行此操作以获得下一个即时渲染。甚至更好地使用 requestAnimationFrame

您可能希望每帧都执行最少的操作以使浏览器快速渲染。避免巨大的循环,缓慢的代码。

接下来你可能会遇到的是,进度速度会根据你的CPU而变化,如果你想无论CPU力量都一样,你会想要开始花时间进入考虑,这是一个完全不同的主题:-)

它减慢了整体进度,因为我现在正在等待 HTML 更新完成,然后再进入下一个循环,但 HTML 中显示的进度现在是准确且光滑的。这是一个权衡。

;(async () => {
  let width = document.getElementById('width')
  let topLoops = 8
  let innerLoops = 3600
  let totalLoops = topLoops * innerLoops
  let currentLoop = 0
  let images = Array.from(Array(topLoops).keys())

  for (let img of images) {
    await new Promise((resolve) => {
      // this setTimeout is actually an image.onload callback
      setTimeout(async() => {
        let i = 0
        while (i < innerLoops) {
          currentLoop++
          await new Promise((resolve) => {
            window.requestAnimationFrame(() => {
              let prcnt = ((100/totalLoops) * currentLoop).toFixed(2)
              width.innerHTML = prcnt + '%'
              width.style.width = prcnt + '%'
              resolve()
            })
          })
          i++
        }
        resolve()
      }, 5000)
    })
  }

  await Promise.all(promises)
})()
#width {
  float:left;
  height: 20px;
  background: red;
  color: black;
  padding: 3px;
}
<div id="width"></div>