Muscardinus
Asynchronous JS (Promise) 본문
Single Threaded Language
JS는 Single Threaded 언어이다. 이것이 무엇을 뜻하냐면, 한번에 한 가지 일 밖에 못 한다고 생각하면된다. 하나의 Call Stack만이 존재하고 그러기에 Synchronous Language라고 부른다.
Synchronous
그렇다면 Single Threaded Language이면 어떠한 단점이 있을까?
예를 들어보자,
만약 우리가 웹페이지를 방문하였는데, Alert 창이 띄워지는 상황이 있다고 생각해보자. 우리는 그 Alert의 OK 버튼을 누르기 전 까지는 다른 어떠한 작업도 할 수 없다. 만약, 모든 것이 Alert 처럼 자신의 행동이 끝나기 전 까지는, 다른 행동을 못하게 막아놓는다면 이용에 상당한 불편함이 있을 것 이다. 이러한 것을 해결하기 위해 Concurrency(병행성) 및 Event Loop이 JS에서 이슈화 됐다.
Event Loop
JS 코드를 Browser에서 작동하면, JS Engine은 코드를 parsing 하기 시작한다. 각 코드는 Call Stack에서 실행되고 끝나면 Pop된다. 하지만, 우리가 흔하게 사용하는 DOM, AJAX, Timeout은 JS 자체적으로 제공해주는 것이 아니라, 브라우저에서 Web API에서 제공해주는 것이다.
따라서, Parser는 이것을 Web API에게 전달하고, Web API가 작업을 맡은 후 Callback Queue에 저장해둔다. 이 작업이 실행되는 동안, JS 코드는 계속 진행하고 있으며, 남은 코드가 완료 되면 Callback Queue에 있는 것들을 실행하게 된다. 이것을 Event Loop가 통제하는 것이다.
console.log("1");
// goes on call stack and runs 1
setTimeout(() => {
console.log("2");
}, 1000);
// gets sent to web api
// web api waits 1 sec, runs and sends to callback queue
// the javascript engine keeps going
console.log("3");
// goes on call stack and runs 3
// event loop keeps checking and see call stack is empty
// event loop sends calback queue into call stack
// 2 is now ran
// 1
// 3
// 2
// Example with 0 second timeout
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
console.log("3");
// 1
// 3
// 2
// Still has the same output
해당 링크로 들어가면 원리를 좀 더 시각적으로 볼 수 있을 것 이다.
http://latentflip.com/loupe/?code=ZnVuY3Rpb24gcHJpbnRIZWxsbygpIHsNCiAgICBjb25zb2xlLmxvZygnSGVsbG8gZnJvbSBiYXonKTsNCn0NCg0KZnVuY3Rpb24gYmF6KCkgew0KICAgIHNldFRpbWVvdXQocHJpbnRIZWxsbywgMzAwMCk7DQp9DQoNCmZ1bmN0aW9uIGJhcigpIHsNCiAgICBiYXooKTsNCn0NCg0KZnVuY3Rpb24gZm9vKCkgew0KICAgIGJhcigpOw0KfQ0KDQpmb28oKTs%3D!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D
Job Queue
// 1 Callback Queue ~ Task Queue
setTimeout(() => {
console.log("1", "is the loneliest number");
}, 0);
setTimeout(() => {
console.log("2", "can be as bad as one");
}, 10);
// 2 Job Queue ~ Microtask Queue
Promise.resolve("hi").then((data) => console.log("2", data));
// 3
console.log("3", "is a crowd");
// 3 is a crowd
// 2 hi
// undefined Promise resolved
// 1 is the loneliest number
// 2 can be as bad as onez
다음 코드를 보면 뭔가 이상함을 느꼈을 것 이다. 왜 Promise가 setTimeout 보다 먼저 되는 것일까?
그 이유는, ES6에서 Promise가 소개되면서, Callback Queue 보다 더 우선 순위가 있는 Job Queue를 만들었기 때문이다. Job Queue는 Promise 계열에 대해서만 추적하며, Callback Queue 보다 항상 우선권이 있다.
3 ways to Promise
promise에는 resolve가 3가지의 방법이 있다. Parallel (다 같이), Sequential(순서대로 하나 씩), Race (가장 빠른 하나)
const promisify = (item, delay) =>
new Promise((resolve) => setTimeout(() => resolve(item), delay));
const a = () => promisify("a", 100);
const b = () => promisify("b", 5000);
const c = () => promisify("c", 3000);
async function parallel() {
const promises = [a(), b(), c()];
const [output1, output2, output3] = await Promise.all(promises);
return `parallel is done: ${output1} ${output2} ${output3}`;
}
async function sequence() {
const output1 = await a();
const output2 = await b();
const output3 = await c();
return `sequence is done: ${output1} ${output2} ${output3}`;
}
async function race() {
const promises = [a(), b(), c()];
const output1 = await Promise.race(promises);
return `race is done: ${output1}`;
}
sequence().then(console.log);
parallel().then(console.log);
race().then(console.log);
// race is done: a
// parallel is done: a b c
// sequence is done: a b c
'FrontEnd > JavaScript Basics' 카테고리의 다른 글
Closures (0) | 2020.12.17 |
---|---|
Higher Order Functions (0) | 2020.12.17 |
Prototype (0) | 2020.12.17 |
Lexical Environment (0) | 2020.12.16 |
Hoisting (0) | 2020.12.16 |