Back

web api와 비동기 처리

콜백함수를 통해 어떤 처리가 이루어진 다음 해당 함수를 실행하려고 했는데, 콜백함수가 먼저 실행되는 것 처럼 보이게 되었다.

라이브러리를 가져다가 사용하는데 이런 문제를 겪었다.
해당 라이브러리는 브라우저 페이지 내에서 네이티브 앱 마냥 윈도우 창을 띄우듯 화면을 띄우고 옮길 수 있다.
어떤 커다란 A 화면을 띄운 이후에 작은 B 화면을 띄우려는 동작을 하였다. 각 화면은 modal과 비슷한 형태이며, 원했던 의도대로라면 A안에 B화면이 떠있는 것 처럼 보여야한다.
하지만, B 화면이 먼저 나와 결론적으로 A 뒤에 덮어 씌워져서 A 화면만 보이게 된다.

좋은 방법은 아니지만, 억지로 나중에 띄울 화면과 관련한 함수를 setTimeout 안에 넣는 것을 통해 100ms 이후에 실행하도록 해보다가 10ms, 1ms 하다가 결국 0까지 내려갔다.
결론적으로 setTimeout(fn, 0) 까지 왔는데, 신기하게 내가 하고싶었던 대로 잘 동작한다.
아니, 0밀리세컨드 쉬었다가 실행하나, 바로 실행하나 차이가 없을텐데 어떻게 이런 결과가 나오게 될까?

그냥 fn을 실행하는 것과 0초 뒤에 실행하는 것의 차이가 있는 줄 알았으나 알고나니 다른 부수적인 변화들이 존재했다.

setTimeout(fn, 0) 과 그냥 바로 fn을 호출하는 것의 차이는 무엇일까?


일단 setTimeout의 밀리세컨드 설정이 최소 단위가 존재한다.

setTimeout 의 밀리세컨드 설정을 0으로 하더라도 0밀리세컨드 이후에 (즉, 바로 실행) 하는 것이 아니라 어느정도의 최소값이 지난 이후에 실행이 된다고 한다.
구형 브라우저의 경우에는 10 밀리세컨드가 최소값이고, 최신 브라우저는 4 밀리세컨드라고 한다.

JS의 비동기처리 방식 때문에 생기는 차이점

JS의 함수 동작 원리에 대해 알아야하는데, 기다려야 하거나, 비동기로 처리하여 나중에 응답을 받는 여러 처리 과정들은 싱글 스레드인 JS 엔진에서 동시에 처리하는 것이 불가능하기 때문에 이를 API를 통해서 브라우저에게 위임을 한다.

그리고 기본적인 JS 함수 호출은 Call Stack에 쌓이며 이를 순차적으로 처리하지만, 위와 같이 브라우저에게 위임한 처리들은 이에대한 일이 끝나면 그 결과와 콜백을 Task Queue에 쌓게 된다. 브라우저는 이후 Call Stack 이 비워질 때 마다 Task Queue에서 가장 오래된 작업을 꺼내와 해당 작업에 대한 콜백을 실행시킨다.

브라우저가 이런 과정을 끊임없이 반복을 하는데 이를 이벤트루프(event loop)라고 부르며
위와같은 과정을 생각해봤을 때 Call Stack이 비워지지 않으면, Task Queue가 작업을 처리할 수 없다.

그래서 기존에 setTimeout을 통해 새롭게 띄우는 화면이 콜백으로 넘기기 전 실행한 화면보다 확실하게 나중에 뜨는 이유는, 미리 띄웠던 큰 화면은 비동기처리하지 않았고, 나중에 띄울 화면은 Call Stack이 모두 처리되면 그제야 실행할 수 있도록 비동기처리로 만들었기 때문이다.

setTimeout(() => {
  console.log('hello');
}, 0);

console.log('world');

// 출력 결과:
// world
// hello