Я уже несколько ночей боролся с проблемой времени в моем коде и не могу придумать правильное решение.
Описание псевдокода:
// прослушиватель событий на кнопке запускает бросок кубика при каждом нажатии
// вызываем функцию анимации // генерировать случайные значения x количество раз // отображаем каждый результат с помощью setTimeout // запускаем код для определения окончательного "установленного" значения и отображения результатов
HTML:
<!DOCTYPE html>
<html lang = "en">
<meta charset = "utf-8">
<head>
<title>Make Cards</title>
</head>
<body>
<p>Total:<span id = "total"></span></p>
<div id = "contain">
<div class = "die"></div>
<div class = "die"></div>
<div class = "die"></div>
<div class = "die"></div>
<div class = "die"></div>
<div class = "die"></div>
<br>
<button id = "rollBtn">Roll</button>
<input type = "number" value = "1" min = "1" max = "6" id = "numDice">
</div>
</body>
</html>
Javascript:
var diceValue = ["⚀", "⚁", "⚂", "⚃", "⚄",
"⚅"];
var dice = document.querySelectorAll(".die");
// select number of dice
var numDice = document.querySelector("#numDice");
// convert string to value
var newNumDice = Number(numDice.value);
var roll = document.querySelector("#rollBtn");
var total = document.querySelector("#total");
// make animation function
// call animation function
init();
function init(){
displayDice();
}
//THIS IS THE BUTTON LISTERNER
roll.addEventListener("click", function(){
//THIS SHOULD RUN BEFORE THE REST OF THE FUNCTION (BUT DOESN'T SEEM TO)
animate();
var subTotal = 0;
for (var i = 0; i < newNumDice; i++){
var value = generateRandomDice()
dice[i].innerHTML = diceValue[value];
subTotal = subTotal + (value+1);
total.innerHTML = subTotal;
}
for (var i = 0; i < 6; i++){
dice[i].style.color = "black";
}
})
numDice.addEventListener("click", function(){
total.innerHTML = "-";
newNumDice = Number(numDice.value);
resetDice();
displayDice();
// console.info(Number(numDice.value));
})
function resetDice(){
for (var i = 0; i < diceValue.length; i++){
dice[i].innerHTML = "";
}
}
// only display chosen number of dice
function displayDice(){
for (var i = 0; i < newNumDice; i++){
dice[i].innerHTML = diceValue[i];
}
}
function generateRandomDice(){
var dieSide = Math.floor(Math.random() * 6);
return dieSide;
}
//ATTEMPT AT WRITING CODE FOR ANIMATION
function animate(){
for (var i = 0; i < 50000; i++){
var ani = setTimeout(rolling, 650);
}
}
function rolling(){
for (var i = 0; i < newNumDice; i++){
var value = generateRandomDice()
dice[i].innerHTML = diceValue[value];
dice[i].style.color = "grey";
}
}
Не уверен, что происходит, но мне кажется, что animate (); код не запускается, пока не завершится остальная часть eventListener. Любая помощь - это здорово, спасибо.
Исходный код не синхронизировался. Я удалю свой комментарий, потому что он был исправлен с помощью приведенного ниже ответа.
Проблема в том, что вы ждете завершения асинхронного кода, прежде чем выполнять синхронный код. Каждый раз, когда вы вызываете setTimeout, он сохраняет функцию, которую нужно вызвать, когда она будет готова к вызову (что составляет 650 мс от «сейчас»!).
Чтобы исправить, вы можете await
, чтобы ваша анимация закончилась. Вот как я заставил его работать с вашим кодом:
Измените animate
на:
function animate() {
return new Promise((resolve, reject) => {
for (let i = 0; i < 50000; i++) {
setTimeout(() => {
rolling();
if (i == 49999)
resolve();
}, 650);
}
});
}
а затем вы можете дождаться разрешения обещания, изменив свой прослушиватель кликов на это:
roll.addEventListener("click", async function() {
await animate();
var subTotal = 0;
for (var i = 0; i < newNumDice; i++) {
var value = generateRandomDice();
dice[i].innerHTML = diceValue[value];
subTotal = subTotal + (value + 1);
total.innerHTML = subTotal;
}
for (var i = 0; i < 6; i++) {
dice[i].style.color = "black";
}
});
Результат, показанный позже, был для меня правильным.
Надеюсь, это поможет!
Promise
получает довольно хорошую поддержку и может быть полифиллирован при необходимости (например, для поддержки IE11). async/await
- это es2017, гораздо более новое дополнение, чем Promise
. Большинство вечнозеленых браузеров имеют приличную поддержку, но если вам нужна более старая поддержка браузеров, ее нельзя заполнить полифиллами. Чтобы использовать его, вам придется перенастроить код. Если это проблема, вы можете использовать синтаксис Promise.then().catch()
. Или вы можете (вздрогнув) использовать обратный вызов, если не хотите беспокоиться о полифилле.
Что ж, сначала позвольте мне сказать спасибо за исправление моего кода. Это действительно исправляет это, как вы предлагаете. Я знал об обещании, но не ожидал, что, похоже, является настоящим решением с точки зрения времени. Не думаю, что я полностью все это понимаю, но это будет моей домашней работой на потом. Теперь мне нужно кое-что исправить, чтобы на броски 6 кубиков не требовалось в 6 раз больше времени, чем для броска одного кубика.
Что не работает? Я запустил ваш код, и похоже, что он делает то, что вы хотите.