The JavaScript Event Loop: Explained - Lee-hyuna/33-js-concepts-kr GitHub Wiki
์๋ฐ์คํฌ๋ฆฝํธ๋ ์น ๋ธ๋ผ์ฐ์ ์ ์คํฌ๋ฆฝํธ ์ธ์ด๋ก ๊ฑฐ์ ๊ณ ์ ์ฑ์ ๊ฐ๊น์์ง์ ๋ฐ๋ผ, ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํธ์์ฉ ๋ชจ๋ธ์ ๋ํ ๊ธฐ๋ณธ ์ดํด์ Ruby, Python ๋ฐ Java์ ๊ฐ์ ์ธ์ด์์ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ๊ฒฌ๋๋ ์์ฒญ ์๋ต ๋ชจ๋ธ๊ณผ ์ฐจ์ด์ ์ ์ดํดํ ์ ์์ต๋๋ค.
์ด ๊ฒ์๋ฌผ์ ํด๋ผ์ด์ธํธ ๋๋ ์๋ฒ์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ์์ ํ๊ณ ์๋ ์น ๊ฐ๋ฐ์๋ฅผ ๋์์ผ๋กํฉ๋๋ค. ์ด๋ฒคํธ ๋ฃจํ์ ์ด๋ฏธ ์ต์ํ๋ค๋ฉด ์ด ๊ฒ์๋ฌผ์ ๋ง์ ๋ถ๋ถ์ด ์ต์ ํ ๊ฒ์ ๋๋ค. ๊ทธ๋ ์ง ์์ ์ฌ๋๋ค์ ์ํด, ๋น์ ์ด ์ผ์์ ์ผ๋ก ์ฝ๊ณ , ์ฐ๋ ์ฝ๋์ ๋ํด ๋ ๋ง์ ์ด์ ๋ฅผ ์ ๊ธฐ ํ ์ ์๋๋ก ๊ธฐ๋ณธ์ ์ธ ์ดํด๋ฅผ ์ ๊ณตํ๊ธฐ๋ฅผ ํฌ๋งํฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์์ ๊ฑฐ์ ๋ชจ๋ I / O๋ ์ฐจ๋จ๋์ง ์์ต๋๋ค.(non-blocking) ์ฌ๊ธฐ์๋ HTTP ์์ฒญ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ๋ฐ ๋์คํฌ ์ฝ๊ธฐ ๋ฐ ์ฐ๊ธฐ๊ฐ ํฌํจ๋ฉ๋๋ค. ์คํ์ single-threaded๋ ๋ฐํ์์ ์์ ์ ์ํํ๋๋ก ์์ฒญํ๊ณ ์ฝ๋ฐฑ ๊ธฐ๋ฅ์ ์ ๊ณตํ ๋ค์ ๋ค๋ฅธ ์์ ์ ์ํํ๋๋ก ์ด๋ํฉ๋๋ค. ์์ ์ด ์๋ฃ๋๋ฉด ์ ๊ณต๋ ์ฝ๋ฐฑ ๊ธฐ๋ฅ๊ณผ ํจ๊ป ๋ฉ์์ง๊ฐ ๋๊ธฐ์ด์ ํฌํจ๋ฉ๋๋ค. ์์ผ๋ก ์ด๋ค ์์ ์์ ๋ฉ์์ง๋ ๋๊ธฐ์ด์์ ์ ์ธ๋๊ณ ์ฝ๋ฐฑ์ด ์์๋ฉ๋๋ค.
์ด ์ํธ์์ฉ ๋ชจ๋ธ์ ์ฌ์ฉ์ ์ธํฐํ์ด์ค("mouchown" ๋ฐ "click"๊ณผ ๊ฐ์ ์ด๋ฒคํธ๊ฐ ์ธ์ ๋ ์ง ํธ๋ฆฌ๊ฑฐ๋ ์ ์๋ ๊ฒฝ์ฐ)์์ ์ ์ด๋ฏธ ์ต์ํ ๊ฐ๋ฐ์์๊ฒ๋ ์น์ ํ ์ ์์ง๋ง, ์๋ฒ์ธก ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ๊ฒฌ๋๋ ๋๊ธฐ์ ์์ฒญ ์๋ต ๋ชจ๋ธ๊ณผ๋ ๋ค๋ฆ ๋๋ค.
HTTP ์์ฒญ์ www.google.com์ ์ ์ฉํ๊ณ ์๋ต์ ์ฝ์์ ์ถ๋ ฅํ๋ ๋ ๋นํธ์ ์ฝ๋๋ฅผ ๋น๊ตํด๋ณด๊ฒ ์ต๋๋ค. ์ฒซ๋ฒ์งธ, Ruby, Faraday:
response = Faraday.get 'http://www.google.com'
puts response
puts 'Done!'
์คํ ๊ฒฝ๋ก๋ ์ฝ๊ฒ ๋ฐ๋ผ ํ ์ ์์ต๋๋ค.
- get ๋ฉ์๋๊ฐ ์คํ๋๊ณ ์๋ต ์ค๋ ๋๊ฐ ์์ ๋ ๋๊น์ง ์คํ ์ค๋ ๋๊ฐ ๋๊ธฐํฉ๋๋ค.
- response์ Google์์ ์์ ๋์ด ๋ณ์๊ฐ ์ ์ฅ๋ ํธ์ถ์์๊ฒ ๋ฐํ๋ฉ๋๋ค.
- ๋ณ์์ ๊ฐ (์ด ๊ฒฝ์ฐ response)์ console์ ์ถ๋ ฅ๋ฉ๋๋ค.
- "Done!"๊ฐ์ด ์ฝ์์ ์ถ๋ ฅ๋ฉ๋๋ค.
Node.js ๋ฐ ์์ฒญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฐ์คํฌ๋ฆฝํธ์์๋ ๋์ผํ ์์ ์ ์ํํด ๋ณด๊ฒ ์ต๋๋ค.
request('http://www.google.com', function(error, response, body) {
console.log(body);
});
console.log('Done!');
์ฝ๊ฐ์ ๋ค๋ฅธ ๋ชจ์๊ณผ ๋งค์ฐ ๋ค๋ฅธํ๋:
- ์์ฒญ ํจ์๊ฐ ์คํ๋์ด ์ต๋ช ํจ์๋ฅผ ์ฝ๋ฐฑ ํจ์๋ก ์ ๋ฌํ์ฌ ํฅํ ์ธ์ ๊ฐ response์ ์ฌ์ฉํ ์์์ ๋ ์คํํฉ๋๋ค.
- "Done!"์ด ์ฆ์ console์ ์ถ๋ ฅ๋ฉ๋๋ค.
- ์ธ์ ๊ฐ๋ response์ด ๋์์ค๊ณ ์ฝ๋ฐฑ์ด ์คํ๋์ด body๋ฅผ console๋ก ์ถ๋ ฅํฉ๋๋ค.
๋น๋๊ธฐ ์์ ์ด ์๋ฃ๋๊ณ ์ฝ๋ฐฑ์ด ์์๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๋์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฐํ์์์ ๋ค๋ฅธ ์์ ์ ์ํ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ฉ๋ชจ๋ฆฌ์์ ์ด๋ฌํ ์ฝ๋ฐฑ์ ์ด๋์ ์์ผ๋ฉฐ ์ด๋ค ์์๋ก ์คํ๋ฉ๋๊น? ๋ฌด์ ๋๋ฌธ์ ๊ทธ๋ค์ ๋ถ๋ ค์ง๋๊น?
์๋ฐ์คํฌ๋ฆฝํธ ๋ฐํ์์๋ ์ฒ๋ฆฌ ํ ๋ฉ์์ง ๋ชฉ๋ก๊ณผ ๊ด๋ จ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ ์ฅํ๋ ๋ฉ์์ง ๋๊ธฐ์ด์ด ์์ต๋๋ค. ์ด๋ฌํ ๋ฉ์์ง๋ ์ฝ๋ฐฑ ๊ธฐ๋ฅ์ด ์ ๊ณต๋๋ฉด ์ธ๋ถ ์ด๋ฒคํธ (๋ง์ฐ์ค๋ฅผ ํด๋ฆญํ๊ฑฐ๋ HTTP ์์ฒญ์ ๋ํ ์๋ต์๋ฐ๋ ๊ฒ๊ณผ ๊ฐ์)์ ๋ํ ์๋ต์ผ๋ก ๋๊ธฐํฉ๋๋ค. ์๋ฅผ ๋ค์ด ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๊ณ ์ฝ๋ฐฑ ํจ์๊ฐ ์ ๊ณต๋์ง ์์ ๊ฒฝ์ฐ ์๋ฌด๋ฐ ๋ฉ์์ง๋ ๋๊ธฐ์ด์ ์ถ๊ฐ๋์ง ์์ต๋๋ค.
๋ฃจํ์์ ๋๊ธฐ์ด์ ๋ค์ ๋ฉ์์ง ( ๊ฐ polling์ "tick"์ด๋ผ๊ณ ํจ)์ ๋ํด polling๋๊ณ ๋ฉ์์ง๊ฐ ๋ฐ์ํ๋ฉด ํด๋น ๋ฉ์์ง์ ๋ํ ์ฝ๋ฐฑ์ด ์คํ๋ฉ๋๋ค.
์ด ์ฝ๋ฐฑ ํจ์์ ํธ์ถ์ ํธ์ถ ์คํ์ ์ด๊ธฐ ํ๋ ์์ผ๋ก ์ฌ์ฉ๋๋ฉฐ JavaScript๊ฐ single-threaded์ด๋ฏ๋ก ์คํ์ ๋ชจ๋ ํธ์ถ์ด ๋ฐํ ๋ ๋๊น์ง ์ถ๊ฐ ๋ฉ์์ง polling ๋ฐ ์ฒ๋ฆฌ๊ฐ ์ค๋จ๋ฉ๋๋ค. ํ์(๋๊ธฐ)ํจ์ ํธ์ถ์ ์๋ก์ด ํธ์ถ ํ๋ ์์ ์คํ์ ์ถ๊ฐํฉ๋๋ค (์ : function init์ function changeColor๋ฅผ ํธ์ถํฉ๋๋ค).
function init() {
var link = document.getElementById("foo");
link.addEventListener("click", function changeColor() {
this.style.color = "burlywood";
});
}
init();
์ด ์์ ์์ ์ฌ์ฉ์๊ฐ 'foo'์์๋ฅผ ํด๋ฆญํ๊ณ "onclick"์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ๋ฉ์์ง(์ฝ๋ฐฑ, changeColor)๊ฐ ๋๊ธฐ์ด์ ํฌํจ๋ฉ๋๋ค. ๋ฉ์์ง๊ฐ ๋๊ธฐ์ด์์ ์ ์ธ๋๋ฉด ์ฝ๋ฐฑ ํจ์ changeColor๊ฐ ํธ์ถ๋ฉ๋๋ค. changeColor๊ฐ return๋๋ฉด (๋๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด) ์ด๋ฒคํธ ๋ฃจํ๊ฐ ๊ณ์๋ฉ๋๋ค. 'foo'์์์ ๋ํ onclick ์ฝ๋ฐฑ์ผ๋ก ์ง์ ๋ changeColor ํจ์๊ฐ ์์ผ๋ฉด ํด๋น ์์๋ฅผ ๊ณ์ ํด๋ฆญํ๋ฉด ๋ ๋ง์ ๋ฉ์์ง(์ฐ๊ด๋ ์ฝ๋ฐฑ changeColor)๊ฐ ๋๊ธฐ์ด์ ํฌํจ๋ฉ๋๋ค.
์ฝ๋์ ํธ์ถ ๋ ํจ์๊ฐ ๋น๋๊ธฐ์ (setTimeout๊ณผ ๊ฐ์)์ด๋ฉด ์ ๊ณต๋ ์ฝ๋ฐฑ์ ์ด๋ฒคํธ ๋ฃจํ์ ํฅํ ํฑ์์ ๋ค๋ฅธ ๋๊ธฐ์ด ๋ฉ์์ง์ ์ผ๋ถ๋ก ๊ถ๊ทน์ ์ผ๋ก ์คํ๋ฉ๋๋ค. ์๋ฅผ ๋ค๋ฉด :
function f() {
console.log("foo");
setTimeout(g, 0);
console.log("baz");
h();
}
function g() {
console.log("bar");
}
function h() {
console.log("blix");
}
f();
setTimeout์ non-blocking ์์ฑ์ผ๋ก ์ธํด ํด๋น ์ฝ๋ฐฑ์ ์ต์ 0 ๋ฐ๋ฆฌ ์ด๋ฅผ ์์ํ๋ฉฐ ์ด ๋ฉ์์ง์ ์ผ๋ถ๋ก ์ฒ๋ฆฌ๋์ง ์์ต๋๋ค.
์ด ์์ ์์ setTimeout์ ์ฝ๋ฐฑ ํจ์ g์ 0 ๋ฐ๋ฆฌ ์ด์ ํ์ ์์์ ์ ๋ฌํ์ฌ ํธ์ถ๋ฉ๋๋ค. ์ง์ ๋ ์๊ฐ์ด ๊ฒฝ๊ณผํ๋ฉด (์ด ๊ฒฝ์ฐ ๊ฑฐ์ ์ฆ์) ์ฝ๋ฐฑ ํจ์๋ก g๋ฅผ ํฌํจํ๋ ๋ณ๋์ ๋ฉ์์ง๊ฐ ๋๊ธฐ์ด์ ํฌํจ๋ฉ๋๋ค.
console ๊ฒฐ๊ณผ๋ "foo", "baz", "blix" ๊ทธ๋ฆฌ๊ณ ์ด๋ฒคํธ ๋ฃจํ์ ๋ค์ ํฑ์ธ "bar"์ ๊ฐ์ต๋๋ค.
๋์ผํ ํธ์ถ ํ๋ ์์์ ๋ ๋ฒ์งธ ์ธ์์ ๋ํด ๋์ผํ ๊ฐ์ ์ ๋ฌํ๋ setTimeout์ ๋ ๋ฒ์ ํธ์ถ์ด ์ํ๋๋ฉด ํด๋น ํธ์ถ์ ์์๋๋ก ๋๊ธฐ์ด์ ํ์๋ฉ๋๋ค.
Web Workers๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณ๋์ ์คํ ์ค๋ ๋๋ก ๋ถ์ฐํ์ฌ ๋ฉ์ธ ์ค๋ ๋๋ฅผ ์์ ๋กญ๊ฒํ์ฌ ๋ค๋ฅธ ์์ ์ ์ํ ํ ์ ์์ต๋๋ค. Worker๋ ๋ณ๋์ ๋ฉ์์ง ๋๊ธฐ์ด, ์ด๋ฒคํธ ๋ฃจํ ๋ฐ ์ด๋ฅผ ์ธ์คํด์คํ ํ ์๋ ์ค๋ ๋์ ๋ณ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ํฌํจํฉ๋๋ค. Worker์ ์ฃผ ์ค๋ ๋ ๊ฐ์ ์์ฌ ์ํต์ ๋ฉ์์ง ์ ๋ฌ์ ํตํด ์ํ๋ฉ๋๋ค. ์ด๋ ๊ธฐ์กด์ ๋ฐ์ํ๋ ์ฝ๋ ์์ ์ ๋งค์ฐ ํก์ฌํฉ๋๋ค.
์ฒซ์งธ, our worker:
// our worker, which does some CPU-intensive operation
var reportResult = function(e) {
pi = SomeLib.computePiToSpecifiedDecimals(e.data);
postMessage(pi);
};
onmessage = reportResult;
๊ทธ๋ฐ ๋ค์ HTML์ ์คํฌ๋ฆฝํธ ํ๊ทธ์ ์๋ ์ฝ๋์ ์ฃผ์ ๋ฌถ์ :
// our main code, in a <script>-tag in our HTML page
var piWorker = new Worker("pi_calculator.js");
var logResult = function(e) {
console.log("PI: " + e.data);
};
piWorker.addEventListener("message", logResult, false);
piWorker.postMessage(100000);
์ด ์์ ์์ ์ฃผ ์ค๋ ๋๋ Worker๋ฅผ ์์ฑํ๊ณ logResult ์ฝ๋ฐฑ ํจ์๋ฅผ "message"์ด๋ฒคํธ์ ๋ฑ๋กํฉ๋๋ค. Worker์์ reportResult ํจ์๋ ์์ฒด "message"์ด๋ฒคํธ์ ๋ฑ๋ก๋ฉ๋๋ค. Worker ์ค๋ ๋๊ฐ ์ฃผ ์ค๋ ๋์์ ๋ฉ์์ง๋ฅผ ๋ฐ์ผ๋ฉด ์์ ์๋ ๋ฉ์์ง์ ํด๋น reportResult ์ฝ๋ฐฑ์ ๋๊ธฐ์ด์ ๋ฃ์ต๋๋ค. ๋๊ธฐ์ด์์ ๋น ์ ธ ๋์ค๋ฉด (logResult ์ฝ๋ฐฑ๊ณผ ํจ๊ป) ์ ๋ฉ์์ง๊ฐ ๋๊ธฐ์ด์ ํฌํจ๋๋ ๊ธฐ๋ณธ ์ค๋ ๋๋ก ๋ฉ์์ง๊ฐ ๋ค์ ๊ฒ์๋ฉ๋๋ค. ์ด ๋ฐฉ๋ฒ์ผ๋ก ๊ฐ๋ฐ์๋ CPU ์ง์ฝ์ ์ธ ์์ ์ ๋ณ๋์ ์ค๋ ๋๋ก ์์ํ์ฌ ๋ฉ์์ง ์ฒ๋ฆฌ ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ๊ณ์ํ๊ธฐ ์ํด ์ฃผ ์ค๋ ๋๋ฅผ ํด์ ํ ์ ์์ต๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์ ํด๋ก์ ๋ ์ฝ๋ฐฑ์ ์คํ์ด ์์ ํ ์๋ก์ด ์ฝ ์คํ์ ์์ฑํ๋๋ผ๋ ์์ฑ ๋ ํ๊ฒฝ์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ์งํ๋ฉด์ ์คํํ ๋ ์ฝ๋ฐฑ์ ๋ฑ๋ก ํ ์์๊ฒ ํด์ค๋๋ค. ํนํ ์ฝ๋ฐฑ์ด ์์ฑ ๋ ๋ฉ์์ง์ ๋ค๋ฅธ ๋ฉ์์ง์ ์ผ๋ถ๋ก ์ฝ๋ฐฑ์ด ํธ์ถ๋๋ค๋ ์ฌ์ค์ ์๊ณ ์์ด์ผํฉ๋๋ค. ๋ค์ ์์ ๋ฅผ ๊ณ ๋ คํ์ญ์์ค.
function changeHeaderDeferred() {
var header = document.getElementById("header");
setTimeout(function changeHeader() {
header.style.color = "red";
return false;
}, 100);
return false;
}
changeHeaderDeferred();
์ด ์์ ์์๋ ๋ณ์ header๋ฅผ ํฌํจํ๋ changeHeaderDeferred ํจ์๊ฐ ์คํ๋ฉ๋๋ค. setTimeoutํจ์๊ฐ ํธ์ถ๋์ด ํฅํ ๋ฉ์์ง ๋๊ธฐ์ด์ ์ฝ 100๋ฐ๋ฆฌ ์ด(0.1์ด)์ ๋ฉ์์ง๊ฐ ์ถ๊ฐ๋ฉ๋๋ค(changeHeader ์ฝ๋ฐฑ ํฌํจ). ๊ทธ๋ฐ ๋ค์ changeHeaderDeferred ํจ์๋ false๋ฅผ ๋ฐํํ์ฌ ์ฒซ ๋ฒ์งธ ๋ฉ์์ง์ ์ฒ๋ฆฌ๋ฅผ ๋๋ด์ง๋ง header๋ณ์๋ ์ฌ์ ํ ํด๋ก์ ๋ฅผ ํตํด ์ฐธ์กฐ๋๋ฉฐ garbage collection์ ์์ง๋์ง ์์ต๋๋ค. ๋ ๋ฒ์งธ ๋ฉ์์ง๊ฐ ์ฒ๋ฆฌ ๋ ๋ (changeHeader ํจ์) ์ธ๋ถ ํจ์์ ๋ฒ์์ ์ ์ธ ๋ header ๋ณ์์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ์งํฉ๋๋ค. ๋ ๋ฒ์งธ ๋ฉ์์ง (changeHeader ํจ์)๊ฐ ์ฒ๋ฆฌ๋๋ฉด ํค๋ ๋ณ์๊ฐ garbage collection์ ์์ง ๋ ์ ์์ต๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์ ์ด๋ฒคํธ ์ค์ฌ ์ํธ ์์ฉ ๋ชจ๋ธ์ ๋ง์ ํ๋ก๊ทธ๋๋จธ๋ค์ด ์ต์ํ ์์ฒญ ์๋ต ๋ชจ๋ธ๊ณผ ๋ค๋ฆ
๋๋ค. ํ์ง๋ง ๋ณด๋ค์ํผ ๋ก์ผ ๊ณผํ??์ ์๋๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๋ ๊ฐ๋จํ ๋ฉ์ธ์ง ๋๊ธฐ์ด ๋ฐ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋ฐ์๊ฐ ๋น๋๊ธฐ์ ์ฝ๋ฐฑ์ ์ค์ฌ์ผ๋ก ์์คํ
์ ๊ตฌ์ถํจ์ผ๋ก์จ ์ธ๋ถ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋์ ์์
์ ์ฒ๋ฆฌํ๋๋ก ๋ฐํ์์ ๋น์ธ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ด๊ฒ์ ๋์์ฑ์ ๋ํ ํ๋์ ์ ๊ทผ ๋ฐฉ์์ ์ง๋์ง ์์ต๋๋ค.
์ด ๊ฒ์ํ์ ๋ ๋ฒ์งธ ๋ถ๋ถ์์๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋์์ฑ ๋ชจ๋ธ์ MRI Ruby(with threads and the GIL), EventMachine(Ruby), Java(threads)์์ ๋ณผ ์ ์๋ ๊ฒ๊ณผ ๋น๊ตํ ๊ฒ์
๋๋ค.
- Check out this presentation I did recently, titled โThe JavaScript Event Loop: Concurrency in the Language of the Webโ
- Concurrency model and Event Loop @ MDN
- An intro to the Node.js platform, by Aaron Stannard