Skip to content

Commit c422fa0

Browse files
Added Callbacks, Event Listeners, and the Event Loop
1 parent 257d2f7 commit c422fa0

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

17_Callbacks_And_EventListeners.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# CHAPTER 17: Callbacks and Event Listeners
2+
3+
**Callback Function :** Functions are first class citizens i.e. take a function `A` and pass it to another function `B`. Here, `A` is a callback function.(Function that is passed on as argument to another function is called callback function.)
4+
5+
```javascript
6+
function B(A) {}
7+
8+
B(function A() {});
9+
```
10+
11+
- JS is a **synchronous** and **single threaded** language e.g. It can do only one thing at a time and in a specific order. But due to callbacks, we can do async things in JS.
12+
13+
- The callback functions are very powerful in JS as they give the access to whole asynchronous world into synchronous single threaded language.
14+
15+
- So, callback functions gives JS the power of asynchronosity.
16+
17+
> `setTimeout(function () {}, 1000)` -> here the anonymous function is a callback function as it is passed to setTimeout and called sometime later in code after certain time (here 1000ms).
18+
19+
- This is how we do async things. JS is a sync language, but it doesn't wait 1 sec for setTimeout to finish before going to code below it. It stores the function, attaches timer and goes down the code.
20+
21+
```javascript
22+
setTimeout(function () {
23+
console.log("timer");
24+
}, 5000);
25+
26+
function x(y) {
27+
console.log("x");
28+
29+
y();
30+
}
31+
32+
x(function y() {
33+
console.log("y");
34+
});
35+
```
36+
37+
OUTPUT
38+
39+
x
40+
41+
y
42+
43+
timer
44+
45+
- In the call stack, first `x` and `y` are present. After completion, they go away and stack is empty. Then after 5 seconds(from beginning) anonymous suddenly pops up in stack i.e. setTimeout
46+
- All 3 functions are executed through call stack. If any operation blocks the call stack, its called **blocking the main thread**
47+
- Say if `x()` takes 30 sec to run, then JS has to wait for it to finish as it has only 1 call stack/1 main thread. _Never block main thread_.
48+
- **Always use async for functions that take time e.g. setTimeout**
49+
50+
- JS has just only one call stack and we can call it main thread. So, everything that is executed on our page, is executed throught the call stack only.
51+
52+
- And if any operation blocks the call stack then that is called as blocking the main thread.
53+
54+
## **Event Listener**
55+
56+
- When we create a button in HTML and attack a clickListener in JS:
57+
58+
_in index.html_
59+
60+
```html
61+
<button id="clickMe">Click Me!</button>
62+
```
63+
64+
_in index.js_
65+
66+
```javascript
67+
document.getElementById("clickMe").addEventListener("click", function xyz() {
68+
//when event click occurs, this callback function is called into callstack
69+
console.log("Button clicked");
70+
});
71+
```
72+
73+
Suppose we want to increase count by 1 each time button clicked.
74+
75+
- Use global variable (not good as anyone can change it)
76+
77+
```javascript
78+
let count = 0;
79+
80+
document.getElementById("clickMe").addEventListener("click", function xyz() {
81+
console.log("Button clicked", ++count);
82+
});
83+
```
84+
85+
- Use closures for data abstraction
86+
87+
```javascript
88+
function attachEventList() {
89+
//creating new function for closure
90+
let count = 0;
91+
92+
document.getElementById("clickMe").addEventListener("click", function xyz() {
93+
console.log("Button clicked", ++count); //now callback function forms closure with outer scope(count)
94+
});
95+
}
96+
97+
attachEventList();
98+
```
99+
100+
#### Garbage Collection and removeEventListeners
101+
102+
- Event listeners are heavy, that means they take up memory.
103+
- Event listeners are heavy as they form closures. So even when call stack is empty, EventListener won't free up memory allocated to _count_ as it doesn't know when it may need _count_ again.
104+
- Event listeners can also invoke closures with scope.
105+
- Event listeners consume a lot of memory which can potentially slow down the website therefore it is good practice to remove if it is not used.
106+
- **So we remove event listeners when we don't need them (garbage collected)**
107+
- onClick, onHover, onScroll all in a page can slow it down heavily.

18_Async_And_EventLoops.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# CHAPTER 18: Asynchronous JS and Event Loops
2+
3+
- JS is synchronous single threaded language. It has one call stack and all the code of JS is executed inside this call stack. It can only do one thing at a time. This call stack is present inside the JS Engine.
4+
5+
> Note that call stack will execeute any execeution context which enters it. Time, tide and JS waits for none. TLDR : Call stack has no timer
6+
7+
**Browser has JS Engine which has Call Stack which has Global exec context, local exec context etc**
8+
9+
- But browser has many other _superpowers_ - Local storage space, Timer, place to enter URL, Bluetooth access, Geolocation access and so on
10+
- Now JS needs some way to connect the callstack with all these superpowers. This is done using **Web APIs**
11+
12+
### WebAPIs
13+
14+
None of the below are part of Javascript! These are extra superpowers that browser has. Browser gives access to JS callstack to use these powers.
15+
16+
> setTimeout(), DOM APIs, fetch(), localstorage, console (yes, even console.log is not JS!!), location and so many more..
17+
18+
- setTimeout() : Timer function
19+
- DOM APIs : eg.Document.xxxx ; Used to access HTML `<html><script><body>.....` DOM tree. (Document Object Manipulation)
20+
- fetch() : Used to make connection with external servers eg. Netflix servers etc.
21+
22+
We get all these inside call stack through _global object_ ie. **window**
23+
24+
- Use window keyword like : window.setTimeout(), window.localstorage, window.console.log() to log something inside console.
25+
- As window is global obj, and all the above functions are present in global object, we don't explicity write _window_ but it is implied
26+
27+
### Workflow
28+
29+
```javascript
30+
// First a GEC is created and put inside call stack.
31+
console.log("Start"); // this calls the console web api (through window) which in turn actually modifies values in console.
32+
33+
setTimeout(function cb() {
34+
//this calls the setT web api which gives access to timer feature. It stores the callback cb() and starts timer.
35+
console.log("Callback");
36+
}, 5000);
37+
38+
console.log("End"); // calls console api and logs in console window. After this GEC pops from call stack.
39+
```
40+
41+
- While all this is happening, the timer is constantly ticking. After it becomes `0`, the callback `cb()` has to run.
42+
- Now we need this `cb` to go into call stack. Only then will it be executed. For this we need **event loop and Callback queue**
43+
44+
### Event Loops and Callback Queue
45+
46+
- `cb()` cannot simply directly go to callstack to be execeuted. It goes through the callback queue when timer expires.
47+
- Event loop checks the callback queue, and if it has element puts it into call stack. It is a _gate keeper_.
48+
- Now `cb()` in callstack is run. Console API is used and log printed
49+
50+
Final console output:
51+
52+
Start
53+
54+
End
55+
56+
Callback
57+
58+
### Same happens for any other event as well (Click, Hover etc). This is the basic workflow.
59+
60+
```javascript
61+
console.log("Start");
62+
document.getElementById("btn").addEventListener("click", function cb() {
63+
// cb() registered inside webapi environment and event(click) attached to it. ie.
64+
// REGISTERING CALLBACK AND ATTACHING EVENT TO IT.
65+
console.log("Callback");
66+
});
67+
68+
console.log("End"); // calls console api and logs in console window. After this GEC pops from call stack.
69+
```
70+
71+
In above code, even after console prints "Start" and "End" and pops GEC out, **the eventListener stays in webapi env**(with hope that user may click it some day) until explicitly removed, or the browser is closed.
72+
73+
### Why need callback queue?
74+
75+
- Why can't event loop directly take cb() and put it in callstack? Suppose user clciks button x6 times. So 6 cb() are put inside callback queue.
76+
- Event loop sees if call stack is empty/has space and whether callback queue is not empty(6 elements here).
77+
- Elements of callback queue popped off, put in callstack, executed and then popped off from call stack.
78+
79+
### fetch() works a bit different than the rest
80+
81+
```javascript
82+
console.log("Start");
83+
84+
// this calls the console web api (through window) which in turn actually modifies values in console.
85+
86+
setTimeout(function cbT() {
87+
console.log("CB Timeout");
88+
}, 5000);
89+
90+
fetch("https://api.netflix.com").then(function cbF() {
91+
console.log("CB Netflix");
92+
});
93+
94+
console.log("End");
95+
```
96+
97+
- Same steps for everything before `fetch()` in above code.
98+
- fetch registers `cbF` into webapi environment along with existing `cbT`.
99+
- `cbT` is waiting for `5000ms` to end so that it can be put inside callback queue. `cbF` is waiting for data to be returned from Netflix servers.
100+
- fetch requests data from Netflix servers, and get data back and now `cbF` ready to be executed.
101+
**We have something called a Microtask Queue**
102+
- It is exactly same as Callback queue, but it has higher priority. Functions in Microtask `Q` are executed earlier than Callback `Q`.
103+
- `cbF` goes inside the Microtask `Q` and not callback `Q`. Once call stack is empty, Event loop gives chance for elements in **both** Microtask Queue and Callback Queue to enter Call Stack.
104+
105+
- In console, first _Start_ and _End_ are printed in console. First `cbF` goes in callstack and "CB Netflix" is printed. `cbF` popped from callstack
106+
- Next `cbT` is removed from callback Q, put in Call Stack, "CB Timeout" is printed, and `cbT` removed from callstack.
107+
108+
### What enters the Microtask Queue ?
109+
110+
- All the callback functions that come through _promises_ go in microtask Q.
111+
- **Mutation Observer:** Keeps on checking whether there is mutation in DOM tree or not, and if there, then it execeutes some callback function.
112+
- Callback functions that come through _promises and mutation observer_ go inside Microtask Queue.
113+
- All the rest goes inside **Callback Queue aka Task Queue**
114+
115+
- If the task in microtask Queue keeps creating new tasks in the queue, element in callback queue never gets chance to be run. This is called **starvation**
116+
117+
### Some Important Questions
118+
119+
1. **When does the event loop actually start ?**
120+
121+
Event loop, as the name suggests, is a single-thread, loop that is _almost infinite_. It's always running and doing its job.
122+
123+
2. **Are only asynchronous web api callbacks are registered in web api environment?**
124+
125+
YES, the synchronous callback functions like what we pass inside map, filter and reduce aren't registered in the Web API environment. It's just those async callback functions which go through all this.
126+
127+
3. **Does the web API environment stores only the callback function and pushes the same callback to queue/microtask queue?**
128+
129+
Yes, the callback functions are stored, and a reference is scheduled in the queues. Moreover, in the case of event listeners(for example click handlers), the original callbacks stay in the web API environment forever, that's why it's adviced to explicitly remove the listeners when not in use so that the garbage collector does its job.
130+
131+
4. **How does it matter if we delay for setTimeout would be 0ms. Then callback will move to queue without any wait ?**
132+
133+
No, there are trust issues with setTimeout(). The callback function needs to wait until the Call Stack is empty. So the 0 ms callback might have to wait for 100ms also if the stack is busy.

0 commit comments

Comments
 (0)