问题
I am making a simple memory card game. If you click on two cards in a row that are the same they stay open. If you click on two cards in a row that are not the same they both close in 1.5s. For that I use setTimeout(); However, if during that 1.5s user clicks on any other card, event listener function starts running again while setTimeout have not finished running for the first time. This crashes the game. How to prevent other code from running until setTimeout finishes running for the first time? Any suggestion is greatly appreciated!
const cards = document.querySelectorAll('.container__small');
let hasFlippedCard = false;
let firstCard, secondCard;
let lockBoard = false;
cards.forEach(function (card) {
return card.addEventListener('click', flipCard);
});
function flipCard(){
this.classList.add('flip');
if(!hasFlippedCard){
//first click
hasFlippedCard = true;
firstCard = this;
}else {
//second click
hasFlippedCard = false;
secondCard = this;
setTimeout(function(){
if(firstCard.dataset.framework !== secondCard.dataset.framework ){
firstCard.classList.remove('flip');
secondCard.classList.remove('flip');
}
}, 1500);
}
}
回答1:
You can't prevent JavaScript code to run while your setTimeout() is waiting. This is the essence of the JavaScript run-to-completion/event loop execution model.
In your case you should have an additional flag to not do anything during that time:
const cards = document.querySelectorAll('.container__small');
let hasFlippedCard = false;
let firstCard, secondCard;
let lockBoard = false; // <<< use this flag
cards.forEach(function (card) {
return card.addEventListener('click', flipCard);
});
function flipCard(){
if(lockBoard) return; // <<< check flag
this.classList.add('flip');
if(!hasFlippedCard){
//first click
hasFlippedCard = true;
firstCard = this;
}else {
//second click
hasFlippedCard = false;
secondCard = this;
lockBoard = true; // <<< set flag
setTimeout(function(){
if(firstCard.dataset.framework !== secondCard.dataset.framework ){
firstCard.classList.remove('flip');
secondCard.classList.remove('flip');
}
lockBoard = false; // <<< unset flag
}, 1500);
}
}
回答2:
function flipCard(){
if (lockBoard) {
return
}
....
end in your setTimeout function
lockBoard = true
setTimeout(function(){
....
lockBoard = false
}, 1500)
回答3:
Use a boolean flag to determine if the timeout is in progress and then don't execute your callback if it is true.
const cards = document.querySelectorAll('.container__small');
let cardIsFlipping = false;
let hasFlippedCard = false;
let firstCard, secondCard;
let lockBoard = false;
cards.forEach(function (card) {
return card.addEventListener('click', flipCard);
});
function flipCard(){
if(cardIsFlipping) {
return;
}
this.classList.add('flip');
if(!hasFlippedCard){
//first click
hasFlippedCard = true;
firstCard = this;
}else {
//second click
hasFlippedCard = false;
secondCard = this;
cardIsFlipping = true;
setTimeout(function(){
if(firstCard.dataset.framework !== secondCard.dataset.framework ){
firstCard.classList.remove('flip');
secondCard.classList.remove('flip');
}
cardIsFlipping = false;
}, 1500);
}
}
回答4:
Try something like this: disable the cards before the setTimeout function and re-enable them after the function executes.
document.querySelectorAll('.container__small').addAttribute("disabled",true);
setTimeout(function(){
if(firstCard.dataset.framework !== secondCard.dataset.framework ){
firstCard.classList.remove('flip');
secondCard.classList.remove('flip');
}
document.querySelectorAll('.container__small').removeAttribute("disabled");
}, 1500);
回答5:
setTimeout
returns a positive integer that identifies the timer created by setTimeout
so if you have something like:
var myTimer = setTimeout(function(){
console.log( 'executed' );
}, 1500);
You can use clearTimeout( timeoutId )
where timeoutId
is the value of myTimer
so something like clearTimeout( myTimer )
will cancel the execution of that timer if hasn't been executed.
So something like:
var myTimer = setTimeout(function(){
console.log( 'executed' );
}, 1500);
if ( myTimerId ) {
clearTimeout( myTimerId );
}
The code that is inside of the if
block can be placed inside of an event listener like a click
as the one above so you can cancel the timer when the event has been clicked again.
来源:https://stackoverflow.com/questions/52824248/how-to-stop-other-code-from-running-until-settimeout-finishes-running