17

Edit: Я сообщил об этом как Chromium ошибка: https://bugs.chromium.org/p/chromium/issues/detail?id=668257JS глобальная переменная «let» не обновляется в функции?

Я создаю небольшую игру холст в JS с врагами, которые могут стрелять. Для тестирования я создал флаг, объявленный глобально как let fancy = true;, чтобы определить, следует ли использовать «фантастический» алгоритм таргетинга. Я сделал это так, чтобы нажатие P переключило этот флаг. Моя основная функция, frame, вызывает другую функцию, autoShoot, пять раз в секунду. autoShoot использует флаг fancy.

Сегодня началось что-то странное; Я не помню, какие изменения внесли его. Иногда, когда я нажимаю P, autoShoot действует, как fancy, не был переключен. Я сделал некоторую отладку и обнаружил, что новое, измененное значение равно, отраженное внутри frame, но в autoShoot, значение не обновляется. Это случается с перерывами, и иногда значение в autoShoot закрепится (без меня ничего не сделало).

Я уменьшил код до следующего, что все еще вызывает проблему для меня. Попробуйте нажать P несколько раз. Для меня эти два значения получить «из синхронизации» и дисплей по-разному после нажатия P только один или два раза:

Screenshot after pressing P on my computer

(Я бегу Chrome «Версия 54.0.2840.99 м» на 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>

Игра вокруг, вот некоторые наблюдения:

  • удаления любого из следующих причин вопроса уйти:
    • любой строки в коде на вершине дела с полотном, даже только комментарий после const ratio
    • пустого для. ..операторы петля: for (let b of bullets) {}
    • изменения let fancy = в var fancy = или просто fancy =
    • взвалив всю вещь из глобальной области (с использованием IIFE, ОНЛ oad handler или scope)
  • Увеличение размера массива bullets увеличивает частоту возникновения проблемы. Я думаю, что это связано с тем, что frame занимает больше времени для выполнения; первоначально, bullets.length было всего 20, но каждая итерация цикла делала некоторые вещи для обновления пули и т. д.

Это происходит на ваших компьютерах? Есть ли для этого логическое объяснение? Я попытался перезагрузить свой браузер, никаких изменений.

+2

nope, никогда не выходит из синхронизации в firefox –

+0

Вопрос немного сложный ... это помогает, если вы предоставляете более простой полный пример, который воспроизводит проблему. –

+0

Я фактически подрезал это до минимального примера, который все еще вызывает поведение. Удаление чего-нибудь еще заставляет его уйти: P – qxz

ответ

0

Я нахожусь на Chrome 54.0.2840.98 на Mac, и это тоже происходит.Я думаю, что это проблема, потому что, если я завершу объявление в соответствии с оператором let в блок {…}, то снипп отлично работает, и оба значения изменяются сразу после нажатия клавиши.

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>

+0

Почему бы не поставить 'let fancy' внутри области блока? – Bergi

+0

Что именно вы имеете в виду под «* Без области блока, это ограничивается только прослушивателем keydown. *"? Что ограничено? – Bergi

+0

@Bergi хорошие вопросы;) Я не эксперт в ES6, но я знаю инструкцию 'let' в отношении блоков. И мой подход был основан на этом, поэтому я играю с ним. – RWAM

2

Как все закомментирована это кажется Chrome вопрос.

Я попытался воспроизвести ту же самую проблему на Chrome версии 45.0.2454.85 m (64-bit) и 44.0.2403.107 m (32-bit) (включение режима строгого курса, конечно), но я не преуспел. Но на версии 54.0.2840.99 m (64-bit) он есть.

И я заметил, что изменение requestAnimationFrame на что-то вроде setInterval также полностью устраняет проблему.

Итак, я предполагаю, что это странное поведение имеет какое-то отношение к Chrome'srequestAnimationFrame на более новых версиях и, возможно, блокирует видимость природы let и функцию подъема.

Я не могу сказать, с какой версии Chrome мы можем увидеть такого рода «ошибки», но я могу предположить, что это может быть версия 52, потому что в этой версии много изменений произошло, как новый метод Garbage collection, встроенная поддержка для es6 и es7 и т. д. Для получения дополнительной информации вы можете посмотреть this video from I/O 2016.

Может, новый Garbage collection метод вызывает этот вопрос, потому что, как они сказали, в вышеупомянутом видео, это связано с браузером кадров, что-то вроде v8 делает GC, когда браузер находится в режиме ожидания, чтобы не касаться рисунка рамок и т. д. И, как мы знаем, requestAnimationFrame - это метод, который на следующем рисунке кадра вызывает callback, возможно, в этом процессе происходит эта странная вещь. Но это всего лишь предположение, и у меня есть нет компетенции сказать что-то серьезное об этом :)

+1

В этом вопросе не должно быть никакого мусора, созданного скриптом, все может статически выделяться (или в стеке), поэтому ничего не собирать. – Bergi