JS/VanillaJS

[VanillaJS] 비동기 setTimeOut 함수와 연속클릭

kth990303 2021. 10. 14. 22:59
반응형

웹개발을 한다면 개발자에게 있어서 비동기 함수는 필수적인 소양이다.

이번 포스팅에서는 내가 바닐라js를 다루면서 setTimeOut 비동기 함수를 사용하면서 겪었던 버그와 그 버그를 해결한 과정을 적어보겠다.

 

간단한 반응체크 측정게임을 만들어보면서 겪은 에러 위주로 포스팅할 것이다.

유튜브 조현영님의 동영상강좌 기능 + 셀프체크 기능에다가 내가 스스로 또다른 setTimeOut 함수를 추가하여 만들어보았다.

아래 깃헙주소에서 전체코드를 볼 수 있다.

https://github.com/kth990303/TH-s-Web/blob/master/es2021/response-check.html

 

GitHub - kth990303/TH-s-Web: 웹프로그래밍 공부

웹프로그래밍 공부. Contribute to kth990303/TH-s-Web development by creating an account on GitHub.

github.com


알고리즘 설명

1. 파란 화면일 땐 게임대기 화면이다. 여기서 클릭하면 빨간 화면이 나오는데, 이 때는 클릭해선 안된다.

2. 1~3초 중에서 랜덤한 시간 전에는 빨간 화면이 뜨고, 후에는 초록화면이 뜬다.

3. 초록화면이 뜨면 가능한 한 빠르게 클릭한다.

게임 대기화면(왼쪽)과 클릭해서는 안되는 빨간 화면(오른쪽)

만약 빨간 화면일 때 클릭하면 아래 화면이 뜬 후, 다시 게임 대기화면으로 이동한다.

빨간 화면일 때 클릭하면 뜨는 화면(왼쪽)과 초록화면일 때 클릭하면 뜨는 화면(오른쪽)


setTimeOut은 어디에 쓰였는가?

1. 파란 화면(대기화면)에서 클릭하고 1~3초 후에 초록화면을 띄워줘야 할 때

2. 빨간화면에서 클릭하면 성급하다는 메시지를 띄우고 1초 후에 게임 대기화면으로 이동시켜줘야할 때

 

강의에서는 첫 번째 setTimeOut만 있었으며,

두 번째 setTimeOut은 내가 따로 추가하여 만들어보았다.

위와 같이 비동기함수가 두 군데에 쓰이기 때문에 주의해야한다.

 

뭐를 주의해야 하냐?

바로 더블클릭, 연속클릭하면 굉장히 많은 setTimeOut이 발동하여 의도치 않은 결과를 낳을 수 있는 버그가 발생한다는 점을 주의해야 한다!


해결방법

비동기함수가 적다면 사실 아래 방법으로 손쉽게 해결할 수 있다.

 

clearTimeOut 함수로 비동기함수를 clear해주자.

 

setTimeOut, setInterval 와 같은 비동기함수는 사실 반환할 때 그 함수의 id를 리턴해준다.

clearTimeOut(id)와 같이 작성할 경우 설정해주었던 비동기함수가 clear되면서 작동하지 않게 해주면 된다.

 

그런데, 비동기함수가 많거나 기능이 좀 많으면 위와 같은 방법으로 해결이 되지 않을 수도 있다고 생각할 수 있다.

나의 경우는 클릭 이벤트가 발생할 때, 빨간 화면에서 파란화면으로 1초 후에 넘어간 뒤에 clearTimeOut을 해주어야 했기 때문에 clearTimeOut을 쓰기에 상당히 애매한 경우였다.

 else if(e.target.classList.contains('ready')){
 	// 1~3초 후에 초록화면이 뜨는 setTimeOut 함수를 clear
   clearTimeout(timeOutId);
   
   $screen.textContent='너무 성급하시군요! 다시 시작해주세요';
   
   // 1초 후에 대기화면으로 이동시켜주고 싶어서 만든 setTimeOut
   // 그러나 현재 화면에서 클릭 이벤트를 다시 발생시키면 이 setTimeOut은 또 생기고 또 생긴다.
   fastTimeOutId = setTimeout(()=>{
     $screen.classList.replace('ready', 'waiting');
     $screen.textContent='클릭해서 시작하세요';
   }, 1000);
}

이런 경우는 사용 후에 id를 null로 초기화시켜 해결하는 방법이 있다.

사실 위 코드는 fastTimeOutId 변수 위에 clearTimeOut(fastTimeOutId);를 하면 해결되는 문제이긴 한데, 

존재하지 않는 id의 setTimeOut 함수를 clear하는 것보단, id가 null인지 체크하고 clear하는 것이 더 안전하므로 id를 사용 후에 null로 바꿔주는 작업을 거쳐주자.

else if(e.target.classList.contains('ready')){
  // timeOutId를 리턴하는 setTimeOut함수가 있을 때에만 clear해주었다.
  if(timeOutId){
    clearTimeout(timeOutId);
    timeOutId=null;
  }
  // fastTimeOutId를 리턴하는 setTimeOut함수가 진행되지 않았거나,
  // 이미 clear가 진행된 경우에만 (즉, 정상적으로 대기화면으로 이동 후 다시 빨간화면 클릭했을 때만)
  // setTimeOut함수를 실행하도록 조건문을 걸어주었다.
  if(fastTimeOutId===null){
    $screen.textContent='너무 성급하시군요! 다시 시작해주세요';
    fastTimeOutId = setTimeout(()=>{
      $screen.classList.replace('ready', 'waiting');
      $screen.textContent='클릭해서 시작하세요';
    }, 1000);
  }
}

이제 빨간화면에서 연속클릭을 하든, 대기화면에서 연속클릭을 아무리 빠르게 날려도

알고리즘의 의도대로 잘 돌아감을 확인할 수 있다.

 

앞으로 더 수많은 비동기함수를 사용해볼텐데, 어서 많은 버그를 발견해서 팍팍 성장했으면 좋겠다.

(아, 물론 공부할 때에만 ^^ 실전에선 버그 안나길 기원)

 

 

반응형