Edit: I've reported this as a Chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=668257
I'm creating a little canvas game in JS with enemies that can shoot. For testing, I created a flag, declared globally as let fancy = true;
, to determine whether or not to use a "fancy" targeting algorithm. I made it so that pressing P will toggle this flag. My main function, frame
, calls another function, autoShoot
, five times per second. autoShoot
uses the fancy
flag.
Today, something strange started happening; I don't remember what change introduced it. Sometimes, when I press P, autoShoot
acts like fancy
didn't get toggled. I did some debugging and discovered that the new, toggled value is reflected inside frame
, but in autoShoot
, the value isn't updated. It happens intermittently, and sometimes the value in autoShoot
will fix itself (without me having done anything).
I've reduced the code to the following, which still exhibits the problem for me. Try pressing P a bunch of times. For me, the two values get "out of sync" and display differently after pressing P just once or twice:
(I'm running Chrome "Version 54.0.2840.99 m" on Windows 10.)
const canvas = document.getElementById("c");
const width = 0;
const height = 0;
const ctx = canvas.getContext("2d");
const ratio =1;// (window.devicePixelyRatio||1)/(ctxFOOOOOOOOFOOOOOOOOOFOOOOO||1);
canvas.width = width*ratio;
canvas.height = height*ratio;
canvas.style.width = width+"px";
canvas.style.height = height+"px";
ctx.scale(ratio, ratio);
function testSet(id, val) {
console.log(id+": "+val);
document.getElementById(id).innerText = val;
}
let fancy = true;
document.body.addEventListener("keydown", function(e) {
if (e.keyCode == 80) {
fancy = !fancy;
console.log("Set fancy to: "+fancy);
}
});
let bullets = Array(2000);
let lastTime = 0, shotTimer = 0;
function frame(time) {
const dt = (time - lastTime)/1000;
lastTime = time;
if ((shotTimer -= dt) <= 0) {
testSet("frame", fancy);
autoShoot();
shotTimer = 0.2;
}
for (let b of bullets) {}
requestAnimationFrame(frame);
}
function autoShoot() {
testSet("autoShoot", fancy);
}
requestAnimationFrame(frame);
<code>
fancy (frame) = <span id="frame"></span><br>
fancy (autoShoot) = <span id="autoShoot"></span>
</code>
<canvas id="c"></canvas>
Playing around, here are some observations:
const ratio
for (let b of bullets) {}
let fancy =
to var fancy =
or just fancy =
bullets
array increases the frequency that the issue occurs. I think it's because it makes frame
take longer to execute; originally, bullets.length
was only 20, but each loop iteration did some stuff to update the bullet, etc.Does this happen on your computers? Is there any logical explanation for this? I've tried restarting my browser, no change.
Functions can access global variables and modify them. Modifying global variables in a function is considered poor programming practice. It is better to send a variable in as a parameter (or have it be returned in the 'return' statement).
Global variables can be accessed from anywhere in a JavaScript program.
If it's been created globally, then you'll be updating the global variable. You can override this behaviour by declaring it locally using var , but if you don't use var , then a variable name used in a function will be global if that variable has been declared globally.
This is because global variables are easily overwritten by other scripts. Global Variables are not bad and not even a security concern, but it shouldn't overwrite values of another variable. On the usage of more global variables in our code, it may lead to a maintenance issue.
As everybody commented out it seems to be a Chrome
issue.
I've tried to reproduce the same issue on Chrome
version 45.0.2454.85 m (64-bit)
and 44.0.2403.107 m (32-bit)
(enabling strict mode of course) but I've not succeded. But on version 54.0.2840.99 m (64-bit)
it is there.
And I noticed that changing requestAnimationFrame
to something like setInterval
also makes the problem totally go away.
So, I assume that this strange behaviour has something to do with Chrome's
requestAnimationFrame
on newer versions, and maybe block scoping nature of let
, and function hoisting.
I can't say from which version of Chrome
we can see this kind of "bug", but I can assume that it can be the version 52
, because in this version many changes occured, like new method of Garbage collection
, native support for es6
and es7
etc. For more information you can watch this video from I/O 2016
.
Maybe, new Garbage collection
method is causing this issue, because as they told in the above mentioned video it is connected with browser frames, something like v8
does GC when the browser is idle, in order not to touch the drawing of frames etc. And as we know that requestAnimationFrame
is a method which calls a callback
on the next frame drawing, maybe in this process these weird thing's happening. But this is just an assumption and I've no competence to say something serious about this:)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With