vanminhquangtri
vanminhquangtri

Reputation: 335

How to reduce CPU usage of web worker HTML 5

I am using web worker (WW) for an animation. I use WW instead of using setInterval to avoid problem setInterval is stopped when tab is in active. But the WW seems to use so much CPU, it always take at least 37 - 38% of CPU. Can you please advise any solution how to reduce this usage? Live demo: https://repl.it/repls/SweetPowerlessKeychanger#worker.js
Below is my code. Thank you so much.

  1. Index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Worker 1</title>
    <style>
        #it {
            width: 50px;
            height: 50px;
            background: red;
            border-radius: 50%;
            position: fixed;
            top: 50px;
        }
    </style>
</head>
<body>
    <div id="it"></div>
    <script src="main.js"></script>
</body>
</html>
  1. Main.js (Mother of the worker):
const it = document.getElementById("it");
it.style.left = "10px";
const maxLeft = window.innerWidth - 50;

const worker = new Worker("./worker.js");
worker.postMessage({
    currentX: 10,
    limit: maxLeft
});
worker.onmessage = (ev) => {
    const data = ev.data;
    it.style.left = data + "px";
    worker.postMessage({
        currentX: parseFloat(it.style.left),
        limit: maxLeft
    })
};
  1. worker.js:
let speed = 0.05;

onmessage = (ev) => {
    const {currentX, limit} = ev.data;
    if (currentX >= limit) {
        speed = -0.05
    }
    if (currentX <= 0) {
        speed = 0.05
    }
    postMessage(currentX + speed)
}

Upvotes: 0

Views: 1287

Answers (1)

Kaiido
Kaiido

Reputation: 136716

It uses a lot of CPU because as has been pointed out in comments, you are asking the Worker to perform a new job right after you received the previous response.

In best case scenario, main is idle, it is able to handle the worker's message directly and posts a new message -> there is not even a single iteration of the worker's event-loop where it has nothing to do.

Depending on your configuration, this also means that you are asking main to update your element's position about a thousands times per screen-refresh rate, really not a great idea.

To workaround that, you should be using some kind of timer in the worker thread.
Recent browsers have added requestAnimationFrame method in this context too, and if it's not available, you can always fallback to using setTimeout.

But note that your whole idea of using a Web-Worker as a mean to work-around the browser's throttling of timers in background's tabs is prone to fail too very soon.
Indeed, browsers have scheduled to also throttle Web-Workers in a near future, and then it will stop working.

Instead you should rather refactor your code so that it uses the elapsed time to define your element's position (a.k.a. time-delta). This way, it doesn't matter if the page is throttled or not, your element is at the position it should have been at this time.

const it = document.getElementById("it");
const checkbox = document.querySelector("input");
const maxLeft = window.innerWidth - 50;
const duration = 2000; // time to traverse the screen (ms)
const start = performance.now();

function anim( now ) {
  const delta = ((now - start) % duration) / duration;
  // determine if we should go to left or to right
  const direction = Math.sign( ((now - start) % (duration * 2)) - duration );

  let position;
  if( direction < 0 ) {
    position = delta * maxLeft;
  }
  else {
    position = maxLeft - (delta * maxLeft);
  }
  it.style.left = position + "px";
  if( checkbox.checked ) {
    requestAnimationFrame( anim );  
  }
}

requestAnimationFrame( anim );
checkbox.oninput = anim;
#it {
    width: 50px;
    height: 50px;
    background: red;
    border-radius: 50%;
    position: fixed;
    top: 50px;
}
<div id="it"></div>
<label>pause/resume<input type="checkbox" checked></label>

Upvotes: 2

Related Questions