# Twice Around The JS Event Loop
- You probably have "bugs" due to this stuff
- This is complicated
- Ask questions!
## Often Heard
- JavaScript is single-threaded
- JavaScript is event-driven
- JavaScript is asynchronous
- JavaScript is non-blocking
- JavaScript runs the same in Node.js & the browser
## What Happens?
```js
const s = new Date().getSeconds();
var elapsed;
setTimeout(() => {
console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
}, 500);
while(true) {
elapsed = new Date().getSeconds() - s;
if (elapsed >= 2) {
console.log("Good, looped for 2 seconds");
break;
}
}
```
## What Happens?
```js
function foo() {
setTimeout(function foo() {
Promise.resolve('foo')
.then(console.log);
}, 0);
}
function bar() {
setTimeout(function bar() {
console.log('bar');
}, 0);
}
function foobar() {
foo();
bar();
}
foobar();
```
![JS runtime general model](/images/js-runtime-general-model.png)
## Browsers
Stack-governed
Task-driven
- Macrotask queue
- Microtask queue
## Macrotasks
- Events
- Timeout/Interval
- XHR
- Window messages
Also called Events or Messages
## Microtasks
- Promises
- Object observers
Also called Jobs
## Animation Tasks
- RequestAnimationFrame
## Browser Event Loop
1. Run the oldest task in macrotask queue until stack bottom.
2. Run _all_ tasks in microtask queue until empty.
3. Once per frame, run _all_ present tasks in animation queue until stack bottom.
4. Run _all_ tasks in microtask queue until empty.
5. Rinse, repeat (loop)
![Browser JS runtime general model](/images/js-runtime-microtask-model.png)
## Browser Event Loop
- Macrotasks created in-loop are processed next loop
- Microtasks created in-loop are processed until queue is empty
- Animation tasks created in-loop are processed next loop
## Stack Bottom
```js
button.addEventListener('click', () => {
Promise.resolve().then(() => { console.log('microtask 1') });
console.log('click macrotask 1');
});
button.addEventListener('click', () => {
Promise.resolve().then(() => { console.log('microtask 2') });
console.log('click macrotask 2');
});
```
## Stack Bottom
```js
button.addEventListener('click', () => {
Promise.resolve().then(() => { console.log('microtask 1') });
console.log('click macrotask 1');
});
button.addEventListener('click', () => {
Promise.resolve().then(() => { console.log('microtask 2') });
console.log('click macrotask 2');
});
button.click();
```
## Browser Non-JS Macrotasks
- Compute styles
- Compute Layout
- Paint
## Visual Macrotasks Ordering
- RequestAnimationFrame (Chrome/FireFox/spec)
- Compute styles
- Compute Layout
- Paint
- RequestAnimationFrame (Safari/IE/Edge)
This matters for changing styles!
## Browser JS Guidelines
- Identify your macro/micro tasks
- Find stack bottom
## Node.js
Queue-governed
Task-driven
- Macrotask queue
- Microtask queue
## Node.js Macrotask Queues
- Timers
- I/O callbacks
- (Poll)
- Immediates
- I/O Close callbacks
## Node.js Microtask Queues
- nextTicks
- Promises
## Node.js Event Loop
1. Run the oldest task in Timers queue until new.
2. Run the oldest task in nextTick queue until empty.
3. Run the oldest task in Promise queue until empty.
4. Repeat 2 and 3 until both are empty.
## Node.js Event Loop 2
1. Run the oldest task in I/O queue until new.
2. Run the oldest task in nextTick queue until empty.
3. Run the oldest task in Promise queue until empty.
4. Repeat 2 and 3 until both are empty.
## Node.js Event Loop 3
1. Run the oldest task in Immediates queue until new.
2. Run the oldest task in nextTick queue until empty.
3. Run the oldest task in Promise queue until empty.
4. Repeat 2 and 3 until both are empty.
## Node.js Event Loop 4
1. Run the oldest task in I/O Close queue until new.
2. Run the oldest task in nextTick queue until empty.
3. Run the oldest task in Promise queue until empty.
4. Repeat 2 and 3 until both are empty.
## Node.js Event Loop
[![Node.js Event Queue Summary image](/images/node-js-queues.png)](https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810)
## Node.js Non-JS Macrotasks
- Poll for I/O
## Node.js Guidelines
- Sort your tasks into queues
- Watch your I/O starvation
## But Why?
- Computing styles
- Automating tests
- Triggering manual events
- Batching Promises
- Ordering callbacks
## Super Fun Quiz
```js
setTimeout(() => console.log('this is setTimeout 1'), 0);
setTimeout(() => {
console.log('this is setTimeout 2');
Promise.resolve().then(() => {
console.log('this is promise added inside setTimeout');
});
}, 0);
setTimeout(() => console.log('this is setTimeout 3'), 0);
setTimeout(() => console.log('this is setTimeout 4'), 0);
setTimeout(() => console.log('this is setTimeout 5'), 0);
Promise.resolve.then(() => console.log('this is promise 1'));
Promise.resolve().then(() => {
Promise.resolve().then(() => {
console.log('this is the inner promise inside promise');
});
});
```
## Bonus Round
Event demultiplexer
- Browser
- `libuv`
## libuv
External modules that makes use of native C++ and libuv is likely to use the thread pool (think: database access).
```js
process.env.UV_THREADPOOL_SIZE = 4; // default
```
## Good Resources
- [Node.js Event Loop Details](https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810)
- [General JS Runtime Model](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Event_loop)
- [Browser JS Event Loop](https://www.youtube.com/watch?v=cCOL7MC4Pl0)
- [Browser JS Event Loop Bugs/Differences](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)
- [Node.js Event Loop](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/)
- [HTML Standard for JS Task Queues](https://html.spec.whatwg.org/multipage/webappapis.html#task-queue)
## We're Hiring @ OfficeLuv!
https://officeluv.github.io
## Thanks!
https://www.andjosh.com/presents