Home > OS >  Logging progress of loop of promises in HTML is not working
Logging progress of loop of promises in HTML is not working

Time:11-25

With the code below I'm trying to document the progress of a series of loops.

However, as you will see, the progress is not documented smoothly in the DOM. Lots of percentage increments are being skipped.

How can I fix that ?

;(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(() => {
        let i = 0
        while (i < innerLoops) {
          ;(function(cloop) {
            window.requestAnimationFrame(() => {
              let prcnt = ((100/totalLoops) * cloop).toFixed(2)
              width.innerHTML = prcnt   '%'
              width.style.width = prcnt   '%'
            })
          })(  currentLoop)
          i  
        }
        resolve()
      }, 5000)
    })
  }

  await Promise.all(promises)
})()

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

CodePudding user response:

It look like you are trying to run 8 promises in parallel but they run one after the other.

It goes like this: 8 promises are created, one after the other. each promise registers a setTimeout for 5000ms.

Once the 5000ms are over there will be 8 callbacks waiting, the runtime will call them one after the other (JavaScript is single threaded).

The first promise will do a 3600 loop, and will write to the console.log 3600 times. Then the next promise callback will be called by the runtime, and the next 3600 loop will do the same... till all 8 loops are over.

You see many console.log because you do that 3600*8 times... you don't see the DOM changes because the browser will render only after the execution is complete.

In order to see dom changes, you will have to "release" the execution of your code, you can do that with setTimeout(..., 0) to get the next immediate render. or even better use requestAnimationFrame

You would want to do the minimum every frame to let the browser render it fast. avoid huge loops, slow code.

Next thing you will probably encounter is that the progress speed changes according to your CPU, if you want to be same no matter the CPU power, you would want to start taking time into consideration, which a totally different subject :-)

CodePudding user response:

It slows down the overall progress, as I'm now waiting for the HTML updates to finish before moving on to the next loop but the progress shown in the HTML is now accurate and smooth. It's a trade off.

;(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>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related