이벤트 루프를 시각적으로 확인 가능한 사이트
- 호출 스택(Call Stack) : 함수들이 실행되는 공간
- 백그라운드: 타이머, 이벤트 리스너 등의 [비동기 함수]들이 들어감. 여러 작업이 동시에 실행될 수 있는 공간
- 태스크 큐 : 비동기 함수들의 Callback 함수들이 들어감 -> 백그라운드의 비동기 함수들의 조건(time-out, click 등)이 충족되었을때, 테스크 큐로 call-back 함수들이 넘어감.

<div id="wrapper"></div>
<script>
const $wrapper = document.querySelector('#wrapper');
const total = parseInt(prompt('카드 개수를 짝수로 입력하세요(최대 20).'));
const colors = [
'red', 'orange', 'yellow', 'green', 'white',
'pink', 'cyan', 'violet', 'gray', 'black',
];
let colorSlice = colors.slice(0, total / 2);
let colorCopy = colorSlice.concat(colorSlice);
let shuffled = [];
let clicked = [];
let completed = [];
let clickable = false;
let startTime;
function shuffle() {
for (let i = 0; colorCopy.length > 0; i += 1) {
const randomIndex = Math.floor(Math.random() * colorCopy.length);
shuffled = shuffled.concat(colorCopy.splice(randomIndex, 1));
}
}
function createCard(i) {
const card = document.createElement('div');
card.className = 'card';
const cardInner = document.createElement('div');
cardInner.className = 'card-inner';
const cardFront = document.createElement('div');
cardFront.className = 'card-front';
const cardBack = document.createElement('div');
cardBack.className = 'card-back';
cardBack.style.backgroundColor = shuffled[i];
cardInner.appendChild(cardFront);
cardInner.appendChild(cardBack);
card.appendChild(cardInner);
return card;
}
function onClickCard() {
if(!clickable || completed.includes(this) || clicked[0] === this) {
return;
}
this.classList.toggle('flipped');
clicked.push(this);
if(clicked.length !== 2) {
return;
}
const firstBackColor = clicked[0].querySelector('.card-back').style.backgroundColor;
const secondBackColor = clicked[1].querySelector('.card-back').style.backgroundColor;
if (firstBackColor === secondBackColor) {
completed.push(clicked[0]);
completed.push(clicked[1]);
clicked = [];
if(completed.length !== total) {
return;
}
const endTime = new Date();
setTimeout(() => {
alert(`축하합니다! ${(endTime - startTime) / 1000}초 걸렸습니다.`)
resetGame();
},500)
return;
}
clickable = false;
setTimeout(() => {
clicked[0].classList.remove('flipped');
clicked[1].classList.remove('flipped');
clicked = [];
clickable = true;
},500)
}
function startGame() {
clickable = false;
shuffle();
for(let i = 0; i < total; i +=1) {
const card = createCard(i);
card.addEventListener('click', onClickCard);
$wrapper.append(card)
}
document.querySelectorAll('.card').forEach((card, index) => {
setTimeout(() => {
card.classList.add('flipped');
}, 1000 + 100 * index);
});
setTimeout(() => {
document.querySelectorAll('.card').forEach((card) => {
card.classList.remove('flipped');
});
clickable = true;
startTime = new Date();
}, 5000);
}
startGame();
function resetGame() {
$wrapper.innerHTML = '';
colorCopy = colorSlice.concat(colorSlice);
shuffled = [];
completed = [];
startGame();
}
</script>