
μλ¬Έ: https://nolanlawson.com/2025/08/31/why-do-browsers-throttle-javascript-timers
μλ°μ€ν¬λ¦½νΈλ₯Ό κ½€ μ€λ μ¬μ©ν΄ μλ€κ³ νλλΌλ setTimeout(0)μ΄ μ€μ λ‘λ setTimeout(0)μ΄ μλλΌλ μ¬μ€μ λλ μ μμ΅λλ€. μ€μ λ‘ μ΄κ²μ 4ms μ΄νμ μ€νλ μ μμ΅λλ€.
const start = performance.now()
setTimeout(() => {
// μλ§λ 4ms
console.log(performance.now() - start)
}, 0)
κ±°μ 10λ
μ Microsoft Edge νμ μμμ λ, λΈλΌμ°μ κ° μ΄λ° λ°©μμ μ±νν μ΄μ λ "λ¨μ©"μ λ°©μ§νκΈ° μν¨μ΄λΌκ³ λ€μμ΅λλ€. μ¦, setTimeoutμ κ³Όλνκ² μ¬μ©νλ μΉμ¬μ΄νΈκ° λ§κΈ° λλ¬Έμ, μ¬μ©μμ λ°°ν°λ¦¬ μλͺ¨λ₯Ό λ§κ³ μνΈμμ©μ μ°¨λ¨νμ§ μκΈ° μν΄ λΈλΌμ°μ κ° 4msλΌλ νΉλ³ν "μ νλ" μ΅μ μκ°κ°μ μ€μ νλ€λ κ²μ΄μμ΅λλ€.
μ΄λ μΌλΆ λΈλΌμ°μ κ° λ°°ν°λ¦¬ μ μμ μ¬μ©νλ κΈ°κΈ°μ λν μ€λ‘νλ§μ λμ΄κ±°λ(ꡬλ²μ Edgeμ κ²½μ° 16ms) λ°±κ·ΈλΌμ΄λ νμ λν μ€λ‘νλ§μ ν¨μ¬ λ 곡격μ μΌλ‘ λμ΄λ μ΄μ (Chromeμ κ²½μ° 1μ΄!)λ₯Ό μ€λͺ ν©λλ€.
κ·Έλ°λ° ν κ°μ§ μλ¬Έμ΄ νμ μ λ₯Ό κ΄΄λ‘νμ΅λλ€. setTimeoutμ΄ κ·Έλ κ² λ¨μ©λλ€λ©΄, μ λΈλΌμ°μ λ setImmediate(RIP), Promises, μ¬μ§μ΄ scheduler.postTask() κ°μ μλ‘μ΄ νμ΄λ¨Έλ₯Ό κ³μ λμ
νμκΉμ? λ§μ½ setTimeoutμ μ±λ₯μ μλμ μΌλ‘ μ νν΄μΌ νλ€λ©΄, κ²°κ΅ μ΄λ° νμ΄λ¨Έλ€λ κ°μ μ΄λͺ
μ λ§μ΄νκ² λλ κ² μλκΉμ?
2018λ
μ μλ°μ€ν¬λ¦½νΈ νμ΄λ¨Έμ λν κΈ΄ κΈμ μ΄ μ μ΄ μμ§λ§, μ΅κ·ΌκΉμ§ μ΄ μ§λ¬Έμ λ€μ κΊΌλ΄λ³Ό λ§ν λλ ·ν μ΄μ λ μμμ΅λλ€. κ·Έλ¬λ€ IndexedDB APIλ₯Ό μμ μλ°μ€ν¬λ¦½νΈλ‘ ꡬνν fake-indexeddb μμ
μ νλ μ€ μ΄ μ§λ¬Έμ΄ λ μ¬λμ΅λλ€. μκ³ λ³΄λ, IndexedDBλ μ΄λ²€νΈ 루νμ λ¨μ μλ μμ
μ΄ μμ λ νΈλμμ
μ μλ 컀λ°νλ €κ³ ν©λλ€. λ€μ λ§ν΄ λͺ¨λ λ§μ΄ν¬λ‘νμ€ν¬κ° λλ ν, κ·Έλ¦¬κ³ μ΄λ ν μμ
(μ¬μ© 'λ§€ν¬λ‘νμ€ν¬'λΌκ³ λΆλ¬λ λ κΉμ?)λ μμλκΈ° μ κ·Έ μμ μ λ
Έλ¦¬λ κ²μ΄μ£ .
μ΄ μμ
μ μννκΈ° μν΄ fake-indexeddbλ Node.jsμμλ setImmediateλ₯Ό μ¬μ©νκ³ (μ΄λ ꡬν λΈλΌμ°μ λ²μ κ³Ό μ μ¬ν¨), λΈλΌμ°μ μμλ setTimeoutμ μ¬μ©νμ΅λλ€. Nodeμμ setImmediateλ κ½€ μ΄μμ μΈλ°, λ§μ΄ν¬λ‘νμ€ν¬ μ΄νμ λ€λ₯Έ νμ€ν¬λ€λ³΄λ€ λ°λ‘ μμ μ€νλκ³ , μ ν(clamping)λ μκΈ° λλ¬Έμ
λλ€. νμ§λ§ λΈλΌμ°μ μμλ setTimeoutμ΄ κ½€ λΉν¨μ¨μ μ
λλ€. ν λ²€μΉλ§ν¬μ λ°λ₯΄λ©΄, Nodeμμλ 300ms 걸리λ μμ
μ΄ Chromeμμ λ¬΄λ € 4.8sλ κ±Έλ Έμ΅λλ€(16λ°° λλ¦° μλ!).
νμ§λ§ 2025λ μ νμ΄λ¨Έ κΈ°μ μ μ΄ν΄λ³΄λ©΄, μ΄λ€ κ²μ μ νν΄μΌ ν μ§ λͺ ννμ§ μμμ΅λλ€. λͺ κ°μ§ μ νμ§λ λ€μκ³Ό κ°μ΅λλ€.
setImmediate– ꡬν Edgeμ IEμμλ§ μ§μλλ―λ‘, μ¬μ©ν μ μμ΅λλ€.MessageChannel.postMessage– afterframeμμ μ¬μ©νλ κΈ°λ²μ λλ€.window.postMessage– μ’μ μμ΄λμ΄μ§λ§, νμ΄μ§μ λ€λ₯Έ μ€ν¬λ¦½νΈκ° λμΌν APIλ₯Ό μ¬μ©ν κ²½μ° μΆ©λμ μΌμΌν¬ μ μμ΄ λ€μ λΆμμ ν©λλ€. κ·ΈλλsetImmediateν΄λ¦¬νμμλ μ΄ λ°©μμ μ¬μ©ν©λλ€.scheduler.postTask– κ²°λ‘ λΆν° λ§νμλ©΄, μ΄κ²μ΄ μ΅μ’ μ νμ΄μμ΅λλ€. μ κ·Έλ°μ§ μ€λͺ ν΄ λλ¦¬κ² μ΅λλ€!
μ΄ μ νμ§λ€μ λΉκ΅νκΈ° μν΄ μ λ κ°λ¨ν λ²€μΉλ§ν¬λ₯Ό μμ±νμ΅λλ€. μ΄ λ²€μΉλ§ν¬μ λν λͺ κ°μ§ μ€μν μ¬νμ λ€μκ³Ό κ°μ΅λλ€.
setTimeout(λ° μ μ¬ν APIλ€)μ μ ν(clamping) λμμ μ λλ‘ νμ νλ €λ©΄, μ¬λ¬ λ² λ°λ³΅ μ€νν΄λ΄μΌ ν©λλ€.
κΈ°μ μ μΌλ‘λ, HTML λͺ μΈμ λ°λΌ 4ms μ νμsetTimeoutμ΄ 5λ² μ΄μ μ€μ²© μ€νλ λλΆν°(μ¦, νsetTimeoutμ΄ λ€λ₯ΈsetTimeoutμ νΈμΆνλ κ²½μ°) μ μ©λ©λλ€.- λ°°ν°λ¦¬ μ¬μ© νΉμ μ μ μ°κ²° μ¬λΆ, λͺ¨λν°μ μ£Όμ¬μ¨, νμ λ°±κ·ΈλΌμ΄λ/ν¬κ·ΈλΌμ΄λ μ¬λΆ λ±μ΄ μ ν λμμ μν₯μ μ€ μ μλ€λ 건 μκ³ μμ§λ§, κ·Έ λͺ¨λ μ‘°ν©μ μ λΆ ν μ€νΈν΄ λ³Έ 건 μλλλ€. μ λ μΆμ΄ μκ³ , μ€νμ€ κ°μ΄μ μ κ³ λͺ κ°μ§ μ€νμ νλ κ²λ μ¬λ°μ§λ§, ν μμΌ λ΄λ΄ κ·Έκ±Έ νκ³ μΆμ§ μμ΅λλ€.
μ΄μ¨λ , μΈ‘μ λ μμΉλ€μ λ€μκ³Ό κ°μ΅λλ€. (λ¨μ: ms, 2021λ ν 16μΈμΉ λ§₯λΆ νλ‘μμ 101ν λ°λ³΅ μΈ‘μ μ μ€μκ°)
| λΈλΌμ°μ | setTimeout |
MessageChannel.postMessage |
window.postMessage |
scheduler.postTask |
|---|---|---|---|---|
| Chrome 139 | 4.2 | 0.05 | 0.03 | 0.00 |
| Firefox 142 | 4.72 | 0.02 | 0.01 | 0.01 |
| Safari 18.4 | 26.73 | 0.52 | 0.05 | ꡬνλμ§ μμ |
μ°Έκ³ : μ΄ λ²€μΉλ§ν¬λ μμ±νκΈ°κ° κ½€ κΉλ€λ‘μ μ΅λλ€! μ²μμλ Promise.allμ μ¬μ©ν΄ λͺ¨λ νμ΄λ¨Έλ₯Ό λμμ μ€ννλλ°, μ΄ λ°©μμ΄ Safariμ μ€μ²© ν΄λ¦¬μ€ν±μ 무λ ₯νμν€λ λ―νκ³ , Firefoxμμλ νμ΄λ¨Έκ° λΆκ·μΉνκ² λμνμ΅λλ€. κ·Έλμ μ§κΈμ κ° νμ΄λ¨Έλ₯Ό λ 립μ μΌλ‘ μ€ννλλ‘ λ²€μΉλ§ν¬λ₯Ό μμ νμ΅λλ€.
μ νν μ«μμ λ무 μ§μ°©ν νμλ μμ΅λλ€. μ€μν μ μ Chromeκ³Ό Firefoxμμλ setTimeoutμ΄ 4msλ‘ μ ν(clamp)λλ€λ κ²μ΄κ³ , λλ¨Έμ§ μΈ κ°μ§ μ΅μ
μ λμ²΄λ‘ λΉμ·ν μ±λ₯μ 보μΈλ€λ μ μ
λλ€. ν₯λ―Έλ‘κ²λ Safariμμλ setTimeoutμ΄ ν¨μ¬ λ κ°νκ² μ νλλ©°, MessageChannel.postMessageλ window.postMessageλ³΄λ€ μ½κ° λ립λλ€ (λ¬Όλ‘ μμμ μΈκΈν μ΄μ λ€ λλ¬Έμ window.postMessageλ μ¬μ ν λ€μ λΆμμ ν λ°©μμ
λλ€).
μ΄λ² μ€νμ ν΅ν΄ μ κ° λΉλ©΄ν μ§λ¬Έμ λν λ΅μ μ»μ μ μμμ΅λλ€. fake-indexeddbλ κΈ°λ³Έμ μΌλ‘ scheduler.postTaskλ₯Ό μ¬μ©νκ³ (μ¬μ©μ± μΈ‘λ©΄μμ μ κ° μ νΈνλ λ°©μμ
λλ€), κ·Έ μΈμλ MessageChannel.postMessageλ window.postMessageλ₯Ό λ체 μ΅μ
μΌλ‘ μ¬μ©νλ κ²μ΄ μ μ ν΄ λ³΄μμ΅λλ€.
(postTaskμ λ€μν μ°μ μμλ₯Ό μ μ©ν΄ μ€νν΄ λ΄€μ§λ§, μ±λ₯μ λλΆλΆ λμΌνμ΅λλ€. fake-indexeddbμ μ¬μ© λͺ©μ μλ κΈ°λ³Έ μ°μ μμμΈ 'user-visible'μ΄ κ°μ₯ μ μ ν΄ λ³΄μκ³ , μ€μ λ²€μΉλ§ν¬μμλ μ΄ κ°μ μ¬μ©νμ΅λλ€.)
νμ§λ§ μ΄ λͺ¨λ μ€νλ μ κ° μλ κΆκΈνλ μ§λ¬Έμ λν λ΅μ΄ λμ§ λͺ»νμ΅λλ€. μΉ κ°λ°μλ€μ΄ μ΄μ°¨νΌ scheduler.postTaskλ MessageChannel κ°μ λ€λ₯Έ λ°©λ²μ μΈ μ μλ€λ©΄, λΈλΌμ°μ λ μ κ΅³μ΄ setTimeoutμ μ ννλ €κ³ ν κΉμ? μ λ μ΄λ¬ν "κ°μ
"μ λν λ
Όμκ° νλ°ν μ§νλλ λΉμ μΉ μ±λ₯ μνΉ κ·Έλ£Ήμ 곡λ μμ₯μ΄μλ μ μΉκ΅¬ Todd Reifsteckμκ² λ¬Όμμ΅λλ€.
κ·Έλ μ€μ§μ μΌλ‘ λ κ°μ§ μ§μμ΄ μ‘΄μ¬νλ€κ³ λ§νμ΅λλ€. νμͺ½μ μΉ κ°λ°μλ€μ΄ μ€μ€λ‘λ₯Ό ν΄μΉμ§ μλλ‘ νμ΄λ¨Έλ₯Ό μ νν΄μΌ νλ€κ³ μκ°νκ³ , λ€λ₯Έ μͺ½μ κ°λ°μλ€μ΄ μ€μ€λ‘μ μ΄λ¦¬μμμ μκ°νλλ‘ λμλ©°, μ΄λ° λ―Έλ¬ν μ ν(ν΄λ¦¬μ€ν±)μ μ€νλ € νΌλλ§ μΌκΈ°νλ€κ³ μ£Όμ₯νμ΅λλ€.
μμ½νμλ©΄, μ΄κ²μ μ±λ₯ κ΄λ ¨ APIλ₯Ό μ€κ³ν λ λ λ°λΌμ€λ κ³ μ μ μΈ λλ λ§μμ΅λλ€.
"μ΄λ€ APIλ λΉ λ₯΄μ§λ§, μ§λ’°(footgun)λ ν¨κ» λ°λΌμ¨λ€." μ¦, μλͺ» μ¬μ©νλ©΄ ν° λ¬Έμ λ₯Ό μΌμΌν¬ μ μλ€λ μλ―Έμ
λλ€.
μ΄ λ΄μ©μ μ κ° μ΄ μ£Όμ μ λν΄ κ°κ³ μλ μ§κ΄κ³Όλ μ λ€μ΄λ§μ΅λλ€. λΈλΌμ°μ κ°μ
μ μΌλ°μ μΌλ‘ μΉ κ°λ°μκ° μ’μ κΈ°λ₯(μ: setTimeout)μ λ무 λ§μ΄ μ¬μ©νκ±°λ λ λμ μ΅μ
μ μ ν μμ§ λͺ»νλ κ²½μ°μ λ°μν©λλ€(ν°μΉ 리μ€λ λ
Όλμ΄ μ’μ μμ
λλ€). κ²°κ΅ λΈλΌμ°μ λ μ¬μ©μλ₯Ό λμ νμ¬ νλνλ "μ¬μ©μ μμ΄μ νΈ"μ΄λ©°, W3Cμ μ΄ν΄κ΄κ³ μ°μ μμ μμΉ(Priority of Constituencies)μ λ°λ₯΄λ©΄ μ΅μ’
μ¬μ©μμ μꡬ μ¬νμ΄ νμ μΉ κ°λ°μμ μꡬ μ¬νλ³΄λ€ μ°μ νλ€λ μ μ λΆλͺ
ν 보μ¬μ€λλ€.
κ·Έλ κΈ΄ ν΄λ, μΉ κ°λ°μλ€μ μ’
μ’
μ¬λ°λ₯Έ λ°©μμΌλ‘ μμ
νκ³ μΆμ΄ ν©λλ€. (μ΄ λΈλ‘κ·Έ κΈλ κ·Έλ° μλμ μΌνμ΄λΌκ³ μ λ μκ°ν΄μ.)
λ€λ§, νμ μ λλ‘ λ λκ΅¬κ° μλ 건 μλλΌμ κ²°κ΅ λμμ μλ μ무 λꡬλ μ§μ΄ λ€κ³ μ΅μ§λ‘ λ¬Έμ λ₯Ό ν΄κ²°νλ €λ€ λ ν° νΌλμ μ΄λνκ³€ ν©λλ€.
μμ
μ²λ¦¬μ μ€μΌμ€λ§μ λν΄ λ λ§μ μ μ΄κΆμ΄ μ£Όμ΄μ§λ€λ©΄ setTimeoutμ μ€μ©ν νμλ μμ΄μ§κ³ , κ·Έ κ²°κ³Ό μΌμ΄ μλ§μ΄ λΌμ κ°μ
μ΄ νμν μν©μ νΌν μ μμ κ²λλ€.
μ μμμΌλ‘λ λΉλΆκ° postTaskλ postMessageλ μ ν μμ΄(throttled λμ§ μκ³ ) μ μ§λ κ²μ
λλ€. Toddκ° λ§νλ λ "μ§μ" μ€μμ, μΈλ°ν νμ€ν¬ μ€μΌμ€λ§ λꡬλ€μ μ 곡νλ Scheduler APIμ μ‘΄μ¬ μμ²΄κ° νμ¬λ "κ°λ°μμκ² λ λ§μ μ μ΄κΆμ μ£Όμ(pro-control)"λ μ§μμ΄ μ£ΌλκΆμ μ₯κ³ μμμ μμ¬νλ λ―ν©λλ€. Toddλ μ΄ APIλ₯Ό λ μ§μμ μ μΆ©μμΌλ‘ λ³΄κ³ μμ΅λλ€. λ¬Όλ‘ λ§μ μ μ΄κΆμ μ 곡νμ§λ§, 무μμμ μΈ νμμμμ΄ μλλΌ λΈλΌμ°μ μ μ€μ λ λλ§ νμ΄νλΌμΈκ³Ό μ λ ¬λ λ°©μμΌλ‘ λμνλ€λ μ μμ μ΄λ μ λ κ· νμ μ΄λ£¨κ³ μλ€λ κ²μ΄μ£ .
λΉκ΄μ μΈ μ λ§μ ννΈμμλ, μ΄ APIμ‘°μ°¨λ κ²°κ΅ μ€μ©λ μ μμ§ μμκΉ νλ μκ°μ΄ λλλ€. μλ₯Ό λ€μ΄, user-blocking μ°μ μμλ₯Ό μ무 μκ° μμ΄ λͺ¨λ κ³³μ μ¬μ©νλ μμΌλ‘μ.
μ΄μ©λ©΄ λ―Έλμλ μ΄λ€ μ§μ·¨μ μΈ λΈλΌμ°μ λ²€λκ° μ€λ‘νλ§ μ μ΄λ₯Ό λ κ°ννκ² λ μ§λ λͺ¨λ¦
λλ€. κ·Έλ¦¬κ³ κ·Έλ‘ μΈν΄ μΉμ¬μ΄νΈλ€μ΄ λ λΉ λ¦ΏλΉ λ¦Ώνκ³ λ°μμ±λ μ’μΌλ©° λ°°ν°λ¦¬ μλͺ¨λ μ€μ΄λ€μλ€λ μ¬μ€μ λ°κ²¬νκ² λλ€λ©΄, λλ€μ "λΈλΌμ°μ κ°μ
(intervention)"μ λ¬Όκ²°μ΄ μ°Ύμμ¬ μλ μκ² μ£ . (κ·Έλκ° λλ©΄, μλ§ μ°λ¦¬ λͺ¨λλ₯Ό κ·Έ νΌλμμ ꡬν΄μ€ scheduler2 API κ°μ κ² νμν΄μ§μ§λ λͺ¨λ₯΄κ² μ΅λλ€!)
μ λ λ μ΄μ μΉ νμ€ μμ μ κΉμ΄ κ΄μ¬νμ§λ μμμ κ·Έμ μΆμΈ‘λ§ ν μ μμ λΏμ λλ€. λΉλΆκ°μ λλΆλΆμ μΉ κ°λ°μλ€μ΄ κ·Έλ¬νλ―, νμ¬ μ λͺ©νλ₯Ό λ¬μ±νλ λ° λμμ΄ λλ APIλ₯Ό μ ννκ³ , μμΌλ‘ λΈλΌμ°μ κ° λ무 λ§μ΄ λ°λμ§ μκΈ°λ₯Ό λ°λ λΏμ΄μ£ . μ°λ¦¬κ° μ‘°κΈλ§ μ μ€νκ² μ κ·Όνκ³ λ무 κ³Όν 무리μλ§ λμ§ μλλ€λ©΄, κ·Έ μ λ λ°λμ―€μ κ³Όν μꡬλ μλλΌκ³ μκ°ν©λλ€.
μ΄ κΈμ μ΄μμ νΌλλ°±μ μ£Όμ Todd Reifsteckμκ² κ°μ¬λ립λλ€.
μ°Έκ³ : μ κ° setTimeoutμ λν΄ λ§ν λͺ¨λ λ΄μ©μ setIntervalμλ κ·Έλλ‘ μ μ©λ©λλ€. λΈλΌμ°μ κ΄μ μμ 보면, μ΄ λμ κ±°μ λμΌν APIμ λλ€.
μ°Έκ³ : μλ―Έκ° μμμ§λ λͺ¨λ₯΄κ² μ§λ§, fake-indexeddbλ Safariμμ MessageChannelμ΄λ window.postMessage λμ setTimeoutμ λ체 μλ¨μΌλ‘ μ¬μ©νκ³ μμ΅λλ€. μμμ μ€λͺ ν λ²€μΉλ§ν¬ κ²°κ³Όμλ λΆκ΅¬νκ³ μ κ° μ€μ λ‘ window.postMessageκ° λ€λ₯Έ λ λ°©λ²λ³΄λ€ λ λμ μ±λ₯μ λ³΄μΈ κ±΄ fake-indexeddb μ체μ λ²€μΉλ§ν¬μμ λΏμ΄μμ΅λλ€. Safariμμλ MessageChannelμ λν΄ λ³λμ μΆκ°μ μΈ μ€λ‘νλ§(throttling)μ΄ μ μ©λλ κ² κ°κ³ , μ΄λ μ κ° λ§λ λ 립 λ²€μΉλ§ν¬μμλ ν¬μ°©λμ§ μμμ΅λλ€. κ·Έλ¦¬κ³ window.postMessageλ μ¬μ ν μλ¬ λ°μ κ°λ₯μ±μ΄ λμ 보μ΄κΈ° λλ¬Έμ κ°μΈμ μΌλ‘λ μ¬μ©μ κΊΌλ¦¬κ³ μμ΅λλ€. κΆκΈν λΆλ€μ μν΄μ μ λ²€μΉλ§ν¬λ₯Ό 곡μ ν©λλ€.
π νκ΅μ΄λ‘ λ νλ°νΈμλ μν°ν΄μ λΉ λ₯΄κ² λ°μλ³΄κ³ μΆλ€λ©΄ Korean FE Articleμ ꡬλ ν΄μ£ΌμΈμ!
'π¨βπ» web.dev > translates' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
| (λ²μ) μ£½μ νλ μμν¬ μ΄λ‘ (dead framework theory) (1) | 2025.12.09 |
|---|---|
| (λ²μ) 리μ‘νΈ μλ² μ»΄ν¬λνΈλ μ λ§ μ±λ₯μ κ°μ ν κΉμ? (0) | 2025.11.20 |
π¬ λκΈ