5P by neo 4시간전 | ★ favorite | 댓글과 토론
  • JavaScript에서 setTimeout(0)은 실제로 즉시 실행되지 않고 최소 4ms 지연되는 경우가 많으며, 이는 남용 방지를 위한 브라우저의 기본 제한임
  • 이런 제약은 웹사이트가 무분별하게 타이머를 남용하여 배터리 소모인터랙션 저하를 일으키는 것을 막기 위한 조치로, 배터리 모드에서는 16ms, 백그라운드 탭에서는 1초로 더 강하게 제한되기도 함
  • 개발자들은 setTimeout의 한계를 우회하기 위해 setImmediate, MessageChannel.postMessage, window.postMessage, scheduler.postTask 등 다양한 대체 타이머 API를 활용해왔음
  • 실제 벤치마크 결과, Chrome과 Firefox는 4ms 클램핑이 적용되지만 MessageChannelscheduler.postTask는 거의 지연 없이 동작하며, Safari는 setTimeout을 더 강하게 제한하는 특징을 보임
  • 근본적으로는 사용자 경험 보호와 개발자 자유 간의 균형 문제로, 현재는 Scheduler API가 표준화된 해법으로 자리잡고 있으나, 남용이 발생하면 새로운 브라우저 개입(Intervention) 이 도입될 가능성도 있음

setTimeout 제한의 배경

  • setTimeout(0)이라도 어뷰징 때문에 실제 실행은 최소 4ms 뒤에 일어나는 경우가 많음
    const start = performance.now()  
    setTimeout(() => {  
      // 약 4ms 후 실행됨  
      console.log(performance.now() - start)  
    }, 0)  
    
  • 이는 무분별한 반복 호출을 막아 배터리 소모와 렌더링 지연을 줄이기 위함
  • 일부 브라우저는 환경에 따라 제한을 강화함
    • 배터리 모드: 구형 Edge에서는 16ms
    • 백그라운드 탭: Chrome에서 최대 1초까지 지연

다른 타이머 API의 등장

  • setImmediate: IE와 구형 Edge에서만 지원, 사실상 단종
  • MessageChannel.postMessage: 별도 채널을 통해 이벤트 루프에 작업 전달
  • window.postMessage: 성능은 좋지만 다른 스크립트와 충돌 위험 있음
  • scheduler.postTask: 최신 브라우저에서 지원되며 가장 안정적인 선택지로 평가됨

벤치마크 결과 (MacBook Pro 2021, 101회 반복 측정)

  • Chrome 139: setTimeout 4.2ms, scheduler.postTask 0ms
  • Firefox 142: setTimeout 4.72ms, scheduler.postTask 0.01ms
  • Safari 18.4: setTimeout 26.73ms, MessageChannel 0.52ms, window.postMessage 0.05ms

fake-indexeddb 사례

  • IndexedDB는 이벤트 루프의 마이크로태스크 종료 직후 트랜잭션 자동 커밋을 원함
  • Node.js의 setImmediate는 이상적이지만 브라우저에서는 setTimeout이 비효율적
  • Chrome에서는 300ms 걸리는 작업이 브라우저에선 4.8초까지 늘어나는 문제 발생
  • 해결책으로 scheduler.postTask를 기본 사용하고, 호환성 위해 MessageChannel/window.postMessage를 폴백으로 채택

브라우저 개입 논쟁

  • 한쪽은 타이머를 제한해야 개발자가 스스로를 보호할 수 있다고 주장
  • 다른 쪽은 개발자가 스스로 측정하고 최적화할 수 있도록 자유를 보장해야 한다고 주장
  • 결국 사용자 우선 원칙에 따라 브라우저는 남용 방지를 위해 개입(intervention)함
  • Scheduler API는 두 입장을 절충해, 개발자에게 세밀한 작업 제어권을 주면서 브라우저 렌더링 파이프라인과 정렬되도록 설계됨

향후 전망

  • postTaskpostMessage는 당분간 스로틀링 없이 유지될 것으로 보임
  • 하지만 user-blocking 같은 높은 우선순위를 남용하면 다시 개입 가능성 존재
  • 장기적으로는 또 다른 scheduler2 같은 대체 API가 필요해질 수도 있음