hmk run dev

setTimeout, setInterval 그리고 자바스크립트의 비동기 처리에 대해... 본문

javascript

setTimeout, setInterval 그리고 자바스크립트의 비동기 처리에 대해...

hmk run dev 2022. 5. 5. 13:48

setTimeout

functino hello () {
	console.log("hellio")
};

setTimeout(hello, 1000)

- 최소 delay 시간(1초) 후에 콜백 함수를 실행시킨다.

 

 

setInterval

let timer = setInterval(() => console.log("hello"), 1000)
// 1초 마다 hello 출력

setTimeout(() => {clearInterval(timer)}, 5000)
// 5초 후에 반복호출 종료

 

- 1초마다 콜백 함수를 반복 호출한다.

 

 

setInterval 대신 재귀 setTimeout을 써야 하는 이유

let timer = setTimeout(function tick () {
	console.log('hello');
    
    let timer = setTimeout(tick, 1000)
}, 1000)

 

- setInterval과 마찬가지로 setTimeout을 재귀적으로 쓰면 함수를 주기적으로 호출할 수 있다.

- setTimeout이 호출되면 브라우저에게 1초 뒤에 tick()을 실행시켜달라고 요청한다.

- 브라우저는 1초 뒤에 queue에 tick()을 넣고 실행시켜달라고 요청한다.

- tick()은 계속해서 재귀 호출된다.

- JS 엔진은 그전에 실행 중인 tick()이 계속 실행 중이라서 queue에 있는 tick()을 스택에 push 할 수 없다.

 

> 사실 나의 의도는 2초마다 tick()을 호출하고 싶었던 건데 위와 같이 코드를 짠다면 나의 의도와 다르게 결과가 나올 수  있다.

 

 

유연성

setTimeout 재귀 호출은 setInterval보다 유연하다.

예를 들어 서버에 요청을 보내는 코드를 작성한다면 서버에 요청이 과도하게 요청될 때는

요청을 보내주는 주기를 늘려주는 것이 바람직하다.

이럴 때, 요청 주기를 늘리기 위해서 setInterval은 delay 값이 고정되어 있기 때문에 clear후에 다시 호출해야 하는

반면에 재귀 setInterval은 내부 로직에서 delay를 늘릴 수 있다.

 

정확한 호출 간격

 

- setInterval의 호출 간격

let i = setInterval(function{ func(i); }, 100)

- 위의 코드는 1초마다 반복 호출될 것을 기대한다

 

하지만, func 함수가 100ms 보다 오래 걸린다면??

 

함수 호출의 종료 - 현재 함수의 시작 사이의 간격을 100ms로 하고 싶었던 건데

100ms 안에 함수 실행시간이 포함되어 있어 실제 함수 실행 간 간격이 100ms 보다 훨씬 길다.

 

> func가 200ms 걸린다면 func함수는 계속 실행될 것이다.

 

 

반면에 재귀적인 setTimeout은 거의 정확한 함수 호출 간 딜레이 시간을 보장할 수 있습니다.

 

- 함수 호출이 종료되고 그다음 함수가 실행되기 직전까지 최소 100ms가 걸린다.

- 이 방법을 사용하면 거의 정확히 고정된 delay를 보장할 수 있다.

 

> setTimeout과 setInterval에 넘겨진 콜백 함수는 가비지 컬렉션에 대상이 되지 않는다.

 

만약 딜레이를 2h로 주면 그동안 GC가 충분히 일어날 수 있는 시간이다.

하지만 갑자기 콜백 함수가 GC 된다는 것은 말이 안 된다.

브라우저는 이 콜백 함수에 대한 레퍼런스를 들고 있어서 GC 되지 않는 것이다.

이 콜백 함수는 lexical 환경 바깥을 참조하고 있기 때문에 그 바깥에 선언된 변수들도 마찬가지로 

GC의 대상이 되지 않는다. 그렇기 때문에 많은 변수와 함수들이 메모리를 계속 차지하고 있을 수 있다.

스케쥴러를 사용하지 않으면 clear로 초기화하라는 게 다 이런 이유 때문인 것 같다.

 

 

setTimeout을 delay 0으로 설정해 실행 순서를 늦춰주기

 

- setTimeout의 콜백으로 넘겨진 함수는 브라우저 API를 거쳐 큐에 삽입되었다가

콜 스택에 있는 함수가 모두 실행되고 난 후에 스택에 삽입되어 실행된다.(이벤트 루프가 스택이 비어있는지 검사)

 

 

무거운 작업을 나눌 때도 유용

let i = 0; let start = Date.now();

function count() { // 약간의 무거운 작업을 해봅시다. 
	(*) do { i++; } while (i % 1e6 != 0); 
    if (i == 1e9) { 
    	alert("Done in " + (Date.now() - start) + 'ms'); 
    } else { 
    	setTimeout(count); // 호출을 스케쥴링합니다. (**) 
    }
} 

count();

 

 

- count() 함수는 내부 로직에서 i를 0에서 ie9까지 증가시키는 굉장히 무거운 작업이다.

- 싱글 스레드인 자바스크립트는 한 번에 하나의 작업만 수행 가능하다.

- 만약 무거운 count() 함수가 실행된다면 엔진은 이 함수 실행 외에는 아무 일도 할 수 없기 때문에

버튼 같은 걸 클릭해도 JS가 동작하지 않고 먹통인 것처럼 현상이 일어날 수 있다.

- 그렇기 때문에 무거운 작업은 나눠서 처리하는 것이 좋은데 이때 유용하게 사용할 수 있는 것이

setTimeout이다.

 

- 0에서 1e9까지 숫자를 증가시키는 작업을 1e6 씩 증가시키는 작업 여러 개로 쪼개는 것이다.

- 그리고 여러 번 setTimeout 함수로 실행시켰기 때문에 중간에 다른 함수들이 치고 들어와서 실행될 수 있다.

- 그래서 위 함수가 실행되는 동시에 유저는 화면과 상호작용할 수 있음

 

- 무거운 작업을 나눠서 실행 or 한 번에 순차적으로 처리하던 총 처리 시간은 거의 비슷하다.

그렇단 말은 무조건 나눠 실행해 UX를 개선하는 방법이 좋을 것이다.

 

let i = 0; 
let start = Data.now();

function count() { // 스케쥴링을 앞으로이동 
	if( i < 1e9 - 1e6 ){
		setTimeout(count);
    }
};

do {i++};

while ( i % 1e6 != 0 );

if( i == 1e9 ) {
	alert("Done in" + (Data.now()) - start + 'ms');
}

 

- 위 코드는 무거운 작업을 먼저 하고 스케쥴링을 했다.

- 그러나 이 코드는 스케쥴링을 먼저 하고 무거운 작업을 진행한다.

앞의 코드와 순서와 바뀌었지만 앞의 코드보다 빠르게 동작한다.

 

> 무거운 작업을 하고 스케쥴링을 하게 되면 그 스케쥴링된 다음 함수가 호출되기 까지 다른 함수들이

중간에 스택에 치고 들어올 수 있다. 작업 간의 딜레이가 어쩔 수 없이 생긴다.!

 

- 하지만 무거운 작업을 하기 전에 스케쥴링을 하게 되면 무거운 작업이 끝날 때쯤에는 큐에서 그다음 스케쥴링된

함수가 대기를 하고 있기 때문에 바로바로 실행이 된다.

 

- 그래서 if문으로 스케쥴링해야 하는 구간을 설정해준 것이다!

 

 

 

 

참고

https://simsimjae.tistory.com/368

 

#10 스케쥴링 : setTimeout과 setInterval의 깊은 이해

https://www.notion.so/simsimjae/10-setTimeout-setInterval-b71a02545c8f44e6afcb6b685d675cd3 아래 글들은 검색엔진 노출을 위한 글입니다. 노션에서 확인하시면 이미지와 함께 정리된 글을 보실수 있습니다...

simsimjae.tistory.com

 

 

 

 

Comments