The JavaScript Event Loop - Lee-hyuna/33-js-concepts-kr GitHub Wiki

The JavaScript Event Loop

이벀트 λ£¨ν”„λŠ” μžλ°” 슀크립트λ₯Ό μ΄ν•΄ν•˜λŠ” 데 κ°€μž₯ μ€‘μš”ν•œ μš”μ†Œ 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€. 이 κ²Œμ‹œλ¬Όμ€ κ°„λ‹¨ν•œ μš©μ–΄λ‘œ μ„€λͺ…ν•©λ‹ˆλ‹€.

λ²ˆμ—­ : https://flaviocopes.com/javascript-event-loop/

Introduction

이벀트 λ£¨ν”„λŠ” μžλ°” 슀크립트λ₯Ό μ΄ν•΄ν•˜λŠ” 데 κ°€μž₯ μ€‘μš”ν•œ μš”μ†Œ 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€.

ν•„μžλŠ” JavaScript둜 μˆ˜λ…„κ°„ ν”„λ‘œκ·Έλž˜λ° ν•΄μ™”μ§€λ§Œ, ν›„λ“œμ—μ„œ μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ μ™„μ „νžˆ μ΄ν•΄ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. 이 κ°œλ…μ„ μžμ„Ένžˆ μ•Œμ§€ λͺ»ν•˜λŠ” 것은 μ™„μ „νžˆ λ©‹μ§€μ§€λ§Œ ν‰μ†Œμ™€ 같이 μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ μ•Œλ©΄ λ„μ›€μ΄λ©λ‹ˆλ‹€. λ˜ν•œμ΄ μ‹œμ μ—μ„œ μ’€ κΆκΈˆν•΄ ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

이 글은 μžλ°” μŠ€ν¬λ¦½νŠΈκ°€ 단일 μŠ€λ ˆλ“œλ‘œ μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€, 그리고 비동기 ν•¨μˆ˜λ₯Ό μ²˜λ¦¬ν•˜λŠ” 방법에 λŒ€ν•΄ μ„€λͺ…ν•©λ‹ˆλ‹€.

JavaScript μ½”λ“œλŠ” 단일 μŠ€λ ˆλ“œλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€. ν•œ λ²ˆμ— ν•œ 가지 일이 λ°œμƒν•©λ‹ˆλ‹€.

μ΄λŠ” λ™μ‹œμ„± λ¬Έμ œμ— λŒ€ν•΄ κ±±μ •ν•˜μ§€ μ•Šκ³  ν”„λ‘œκ·Έλž˜λ°ν•˜λŠ” 방법을 λ‹¨μˆœν™”ν•˜λ―€λ‘œ μ‹€μ œλ‘œ 맀우 μœ μš©ν•©λ‹ˆλ‹€.

μ½”λ“œ μž‘μ„± 방법에 주의λ₯Ό κΈ°μšΈμ—¬μ•Όν•˜λ©° 동기식 λ„€νŠΈμ›Œν¬ ν˜ΈμΆœμ΄λ‚˜ λ¬΄ν•œ 루프와 같이 μŠ€λ ˆλ“œλ₯Ό 차단할 μˆ˜μžˆλŠ” μš”μ†ŒλŠ” ν”Όν•˜μ‹­μ‹œμ˜€.

일반적으둜 λͺ¨λ“  λΈŒλΌμš°μ € 탭에 λŒ€ν•œ 이벀트 λ£¨ν”„κ°€μžˆμ–΄ λͺ¨λ“  ν”„λ‘œμ„ΈμŠ€λ₯Ό κ²©λ¦¬ν•˜κ³  λ¬΄ν•œ 루프 λ˜λŠ” 무거운 μ²˜λ¦¬κ°€μžˆλŠ” μ›Ή νŽ˜μ΄μ§€κ°€ 전체 λΈŒλΌμš°μ €λ₯Ό block μ‹œν‚€λŠ” 것을 ν”Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이 ν™˜κ²½μ€ μ—¬λŸ¬ 개의 λ™μ‹œμ„±(concurrent) 이벀트 루프λ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ API ν˜ΈμΆœμ„ λ‹€λ£Ήλ‹ˆλ‹€. μ›Ή μ›Œμ»€λŠ” 자체 이벀트 λ£¨ν”„μ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€.

주둜 μ½”λ“œκ°€ 단일 이벀트 λ£¨ν”„μ—μ„œ μ‹€ν–‰λ˜κ³  μ½”λ“œλ₯Ό μ°¨λ‹¨ν•˜μ§€ μ•Šλ„λ‘μ΄ μ½”λ“œλ₯Ό 염두에 λ‘μ–΄μ•Όν•©λ‹ˆλ‹€.

Blocking the event loop

이벀트 λ£¨ν”„λ‘œ μ»¨νŠΈλ‘€μ„ 되돌리기 μœ„ν•΄ λ„ˆλ¬΄ 였래 κ±Έλ¦¬λŠ” JavaScript μ½”λ“œλŠ” νŽ˜μ΄μ§€μ˜ λͺ¨λ“  JavaScript μ½”λ“œ 싀행을 μ°¨λ‹¨ν•˜κ³  UI μŠ€λ ˆλ“œλ₯Ό μ°¨λ‹¨ν•˜λ©° μ‚¬μš©μžλŠ” ν΄λ¦­ν•˜κ±°λ‚˜ νŽ˜μ΄μ§€λ₯Ό 슀크둀 ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

JavaScript의 거의 λͺ¨λ“  I / O ν”„λ¦¬λ―Έν‹°λΈŒλŠ” λΉ„ λΈ”λ‘œν‚Ήμž…λ‹ˆλ‹€. λ„€νŠΈμ›Œν¬ μš”μ²­, Node.js 파일 μ‹œμŠ€ν…œ μž‘μ—… λ“±. 이런 λΈ”λ‘œν‚Ήμ΄ μ˜ˆμ™Έμ΄λ―€λ‘œ JavaScriptλŠ” μ½œλ°±μ— κΈ°λ°˜ν•˜κ³  있으며, μ΅œκ·Όμ—λŠ” promises 및 async/await 에 κΈ°λ°˜ν•©λ‹ˆλ‹€.

The call stack

호좜 μŠ€νƒμ€ LIFO λŒ€κΈ°μ—΄ (λ§ˆμ§€λ§‰ λ“€μ–΄κ°€κ³ , μ²«λ²ˆμ§Έκ°€ λ‚˜μ˜¨λ‹€.)μž…λ‹ˆλ‹€.

이벀트 λ£¨ν”„λŠ” 호좜 μŠ€νƒμ„ 계속 κ²€μ‚¬ν•˜μ—¬ μ‹€ν–‰ν•΄μ•Όν•˜λŠ” function이 μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.

κ·Έλ ‡κ²Œν•˜λŠ” λ™μ•ˆ 호좜 μŠ€νƒμ— 찾은 λͺ¨λ“  ν•¨μˆ˜ ν˜ΈμΆœμ„ μΆ”κ°€ν•˜κ³  각각을 μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•©λ‹ˆλ‹€.

디버거 λ˜λŠ” λΈŒλΌμš°μ € μ½˜μ†”μ—μ„œ μ΅μˆ™ν•œ 였λ₯˜ μŠ€νƒ 좔적을 μ•Œκ³  μžˆμŠ΅λ‹ˆκΉŒ? λΈŒλΌμš°μ €λŠ” 호좜 μŠ€νƒμ—μ„œ ν•¨μˆ˜ 이름을 μ°Ύμ•„ μ–΄λ–€ ν•¨μˆ˜κ°€ ν˜„μž¬ ν˜ΈμΆœμ„ μ‹œμž‘ν–ˆλŠ”μ§€ μ•Œλ €μ€λ‹ˆλ‹€.

https://flaviocopes.com/javascript-event-loop/exception-call-stack.png

A simple event loop explanation

예제λ₯Ό 선택해 λ³΄κ² μŠ΅λ‹ˆλ‹€.

λ‚˜λŠ”foo,bar와bazλ₯Ό _random names_둜 μ‚¬μš©ν•©λ‹ˆλ‹€. 그듀을 λŒ€μ²΄ ν•  이름을 μž…λ ₯ν•˜μ‹­μ‹œμ˜€

const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  bar()
  baz()
}

foo()

ν”„λ¦°νŠΈ λœκ±°λŠ” μ•„λž˜μ™€ κ°™λ‹€.

foo
bar
baz

μ΄λ ‡κ²Œ 될것이닀.

이 μ½”λ“œκ°€ μ‹€ν–‰λ˜λ©΄, λ¨Όμ €foo ()κ°€ ν˜ΈμΆœλ©λ‹ˆλ‹€. foo ()λ‚΄λΆ€μ—μ„œ λ¨Όμ €bar ()λ₯Ό 호좜 ν•œ λ‹€μŒbaz ()λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.

이 μ‹œμ μ—μ„œ 호좜 μŠ€νƒμ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

https://flaviocopes.com/javascript-event-loop/call-stack-first-example.png

λͺ¨λ“  λ°˜λ³΅μ—μ„œ 이벀트 λ£¨ν”„λŠ” 호좜 μŠ€νƒμ— 무언가가 μžˆλŠ”μ§€ μ°Ύμ•„μ„œ μ‹€ν–‰ν•©λ‹ˆλ‹€.

https://flaviocopes.com/javascript-event-loop/execution-order-first-example.png

콜 μŠ€νƒμ΄ λΉ„μ–΄μ§ˆλ•Œ κΉŒμ§€ μ‹€ν–‰λœλ‹€.

Queuing function execution

μœ„μ˜ μ˜ˆλŠ” μ •μƒμ μœΌλ‘œ λ³΄μž…λ‹ˆλ‹€. νŠΉλ³„ν•œ 것은 μ—†μŠ΅λ‹ˆλ‹€. JavaScriptλŠ” μ‹€ν–‰ν•΄μ•Ό ν•  것을 μ°Ύκ³ , μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•©λ‹ˆλ‹€.

μŠ€νƒμ΄ κΉ¨λ—ν•΄μ§ˆ λ•ŒκΉŒμ§€ ν•¨μˆ˜λ₯Ό μ—°κΈ°ν•˜λŠ” 방법을 μ‚΄νŽ΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

`setTimeout (() => {}), 0)의 유슀 μΌ€μ΄μŠ€λŠ” ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” κ²ƒμ΄μ§€λ§Œ, μ½”λ“œμ˜ λ‹€λ₯Έ λͺ¨λ“  ν•¨μˆ˜κ°€ μ‹€ν–‰λ˜λ©΄ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” 것이닀.

이 예제λ₯Ό 보자.

const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  baz()
}

foo()

이 μ½”λ“œλŠ” μ•„λ§ˆ λ†€λžκ²Œ 인쇄 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

foo
baz
bar

이 μ½”λ“œκ°€ μ‹€ν–‰λ˜λ©΄ λ¨Όμ € foo ()κ°€ ν˜ΈμΆœλ©λ‹ˆλ‹€. foo () λ‚΄λΆ€μ—μ„œ μš°λ¦¬λŠ” λ¨Όμ € barλ₯Ό 인수둜 μ „λ‹¬ν•˜μ—¬ setTimeout을 ν˜ΈμΆœν•˜κ³  타이머가 0이 될 λ•ŒκΉŒμ§€ κ°€λŠ₯ν•œ ν•œ 빨리 μ‹€ν–‰ν•˜λ„λ‘ μ§€μ‹œν•©λ‹ˆλ‹€. 그런 λ‹€μŒ baz ()λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.

이 μ‹œμ μ—μ„œ 호좜 μŠ€νƒμ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

https://flaviocopes.com/javascript-event-loop/call-stack-second-example.png

λ‹€μŒμ€ ν”„λ‘œκ·Έλž¨μ˜ λͺ¨λ“  ν•¨μˆ˜μ— λŒ€ν•œ μ‹€ν–‰ μˆœμ„œμž…λ‹ˆλ‹€.

https://flaviocopes.com/javascript-event-loop/execution-order-second-example.png

μ™œ 이런 일이 λ°œμƒν• κΉŒμš”?

The Message Queue

setTimeout ()이 호좜되면 Browser λ˜λŠ” Node.jsκ°€ 타이머λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€. 타이머가 만료되면이 경우 μ¦‰μ‹œ 0으둜 μ„€μ •ν•˜μ—¬ 콜백 ν•¨μˆ˜κ°€ Message Queue에 μ €μž₯λ©λ‹ˆλ‹€.

Message QueueλŠ” 클릭 이벀트 λ‚˜ ν‚€λ³΄λ“œ 이벀트 λ˜λŠ” 응닡 κ°€μ Έ μ˜€κΈ°μ™€ 같은 μ‚¬μš©μžκ°€ μ‹œμž‘ν•œ μ΄λ²€νŠΈκ°€ μ½”λ“œμ— 응닡 ν•  κΈ°νšŒκ°€ 였기 전에 λŒ€κΈ°ν•©λ‹ˆλ‹€. λ˜λŠ” onLoad와 같은 DOM 이벀트.

λ£¨ν”„λŠ” 호좜 μŠ€νƒμ— μš°μ„  μˆœμœ„λ₯Ό λΆ€μ—¬ν•˜κ³  호좜 μŠ€νƒμ—μ„œ 찾은 λͺ¨λ“  것을 λ¨Όμ € μ²˜λ¦¬ν•˜κ³  거기에 아무 것도 μ—†μœΌλ©΄ λ©”μ‹œμ§€ λŒ€κΈ°μ—΄μ—μ„œ ν•­λͺ©μ„ μ„ νƒν•©λ‹ˆλ‹€.

μš°λ¦¬λŠ” setTimeout, fetch λ˜λŠ” λ‹€λ₯Έ 것듀을 λΈŒλΌμš°μ €κ°€ μ œκ³΅ν•˜κΈ° λ•Œλ¬Έμ— μžμ‹ μ˜ μž‘μ—…μ„ μˆ˜ν–‰ ν•  λ•ŒκΉŒμ§€ 기닀릴 ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, setTimeout μ‹œκ°„ 초과λ₯Ό 2 초둜 μ„€μ •ν•˜λ©΄ 2 초λ₯Ό 기닀릴 ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€. λŒ€κΈ°λŠ” λ‹€λ₯Έ κ³³μ—μ„œ λ°œμƒν•©λ‹ˆλ‹€.

ES6 Job Queue

ECMAScript 2015λŠ” Promisesκ°€ μ‚¬μš©ν•˜λŠ” μž‘μ—… λŒ€κΈ°μ—΄ κ°œλ…μ„ λ„μž…ν–ˆμŠ΅λ‹ˆλ‹€ (ES6 / ES2015μ—μ„œλ„ μ†Œκ°œ 됨). 이것은 호좜 μŠ€νƒμ˜ 끝에 λ†“κΈ°λ³΄λ‹€λŠ” κ°€λŠ₯ν•œ ν•œ 빨리 비동기 ν•¨μˆ˜μ˜ κ²°κ³Όλ₯Ό μ‹€ν–‰ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

ν˜„μž¬ ν•¨μˆ˜κ°€ λλ‚˜κΈ° 전에 ν•΄κ²°λ˜λŠ” 약속은 ν˜„μž¬ ν•¨μˆ˜ λ°”λ‘œ λ‹€μŒμ— μ‹€ν–‰λ©λ‹ˆλ‹€.

λ‚˜λŠ” μœ μ›μ§€μ—μ„œ 둀러 μ½”μŠ€ν„° νƒ€κΈ°μ˜ λΉ„μœ λ₯Ό 잘 μ°Ύμ•˜μŠ΅λ‹ˆλ‹€. λ©”μ‹œμ§€ νλŠ” λŒ€κΈ°μ—΄μ˜ λ’€μͺ½μ—, λ‹€λ₯Έ λͺ¨λ“  μ‚¬λžŒλ“€ λ’€μ—μ„œ 당신을 κΈ°λ‹€λ¦½λ‹ˆλ‹€. λ‹Ήμ‹ μ˜ μ°¨λ‘€λ₯Ό κΈ°λ‹€λ €μ•Όν•˜λ©°, μž‘μ—… λŒ€κΈ°μ—΄μ€ 패슀트 패슀 ν‹°μΌ“μž…λ‹ˆλ‹€ 당신이 μ΄μ „μ˜ 것을 끝내 자마자 당신은 λ‹€λ₯Έ νƒ€λŠ” 것을 곧 μ·¨ν•  μˆ˜μžˆλ‹€.

const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  new Promise((resolve, reject) =>
    resolve('should be right after baz, before bar')
  ).then(resolve => console.log(resolve))
  baz()
}

foo()

좜λ ₯은 λ‹€μŒκ³Ό κ°™λ‹€

foo
baz
should be right after baz, before bar
bar

μ΄λŠ” promises (약속에 κΈ°λ°˜ν•œ Async / await)와 setTimeout () λ˜λŠ” λ‹€λ₯Έ ν”Œλž«νΌ APIλ₯Ό ν†΅ν•œ ν‰λ²”ν•œ 였래된 비동기 ν•¨μˆ˜μ˜ 큰 μ°¨μ΄μ μž…λ‹ˆλ‹€.