JavaScript_Conc
JavaScript_Conc
-----------------------------
https://roadsidecoder.hashnode.dev/javascript-interview-questions-promises-and-its-
polyfills?source=more_articles_bottom_blogs
B: Polyfills: (Done)
-------------------------
1. Polyfills - Promise AllSettled, bind(), apply() and call()
2. Execution Context, How JS is executed & CallStack, JS engine Exposed.
Google V8 architecure.
Objects:
----------
https://roadsidecoder.hashnode.dev/javascript-interview-questions-objects-output-
based-destructuring-object
DOM Manipulation:
-----------------
https://www.freecodecamp.org/news/the-javascript-dom-manipulation-handbook/
https://www.freecodecamp.org/news/dom-manipulation-in-javascript/
Arindam Paul
attached screenshot in Document folder
JS VM INTERNALS:
---------------
Eventloop, Async and Scopechains
science part -
art part - understand implications of code.
Agenda:
--------
1. JS interpretation and Memory Model
2. Compilation and Variable Hosting
3. Function Execution
4. Scope Chains and closure.
5. JavaScript Runtime and Event loop
6. Async Examples
7. Single Threaded parallel Execution.
Example Code:
--------------
Global Scope(Window)
a
f ------> lamba exp
---> pointing to some text object in memory
which I am calling
at this point lambda f
In second phase:
----------------
1.value 2 wil be assinged
2. now line go to b = 1;
3. JS runtime does is will ask from memory do have refrence of be
it will say no , but JS will forgive, if you are in Global scope
if you dont have reference of vairable, it will create variable for u
this happens at runtime, execuation phase not compliation time
it will directly reach to f(1) //18
z becomes 1
now what about b = 3 ? , it will check in lexical or global space.
z 1
d 6
g -------> lamba d ----> Local execuation for g() will be created ---->
e 0
e 1
e 1
f(1) // 18
a 2
var a = 2;
b = 1; //this is not variable dec
function f(z) {
b = 3;
c = 4;
var d = 6;
e = 1;
function g() {
var e = 0;
d = 3*d;
return d;
}
return g();
var e; // variable hosting
}
f(1); //18
//2nd Scenerio
----------------
var a = 2;
b = 1; //this is not variable dec
function f(z) {
b = 3;
c = 4;
var d = 6;
e = 1;
function g() {
var e = 0;
d = 3*d;
return d;
}
return g();
var e; // variable hosting
}
-------------------------------
z 1
d 6
g ------> lambda g
e 1
Closure:
--------
What happens when a function is executed outside of its
orginial scope chains ?
Moreover, what happens when a variable hold on to a function
reference which get defined in such deep function
execution chain.
Implicit closure:
------------------
var data = "My Data!";
setTimeout(function() {
console.log(data);// print "My Data!"
},3000);
Explicit Closures:
------------------
function makeAdder(n) {
var inc = n;
var sum = 0;
return function add() {
sum = sum + inc;
return sum;
};
}
var adder3 = makeAdder(3);
console.dir(adder3);
function process(num) {
//function to delay
delay();
console.log(num);
//Synchronous
[1,2,3,4,5,6].forEach(function(i) {
process(i);
}};
//Asynchornous
function process(num) {
//function to delay
delay();
console.log(num);
//Synchronous
[1,2,3,4,5,6].forEach(function(i) {
setTimeout(function() {
process(i);
},o)
process(i);
});
Example:
const button = document.querySelector("#btn");
button.addEventListener("click", () => {
console.log(this); // Logs the outer scope (e.g., Window object)
});
Here, this does not refer to the button element. Instead, it refers to the this
value of the scope in which the arrow function was defined.
Why?
Arrow functions capture this from their surrounding context and do not redefine it.
Example:
javascript
Copy code
const obj = {
value: 42,
init() {
const button = document.querySelector("#btn");
button.addEventListener("click", function () {
console.log(this); // Logs the button element
console.log(obj.value); // Error: obj is not defined
});
obj.init();
In the first handler (regular function), this refers to the button, and obj is not
accessible.
In the second handler (arrow function), this refers to the obj because the arrow
function captures this from the surrounding init() method.
4. Using bind to Control this
You can use bind to explicitly set the value of this.
Example:
javascript
Copy code
const obj = {
value: 42,
};
function handleClick() {
console.log(this.value);
}
Example:
--------
javascript
Copy code
const parent = document.querySelector("#parent");
Example:
html
Copy code
<button onclick="console.log(this)">Click Me</button>
Logs the button element.
b. addEventListener with Class Methods
When using class methods, be cautious of this losing its context.
handleClick() {
console.log(this.value);
}
attachHandler() {
this.button.addEventListener("click", this.handleClick);
}
}
javascript
Copy code
class Example {
constructor() {
this.value = 42;
this.button = document.querySelector("#btn");
this.handleClick = this.handleClick.bind(this); // Bind in the constructor
}
handleClick() {
console.log(this.value);
}
attachHandler() {
this.button.addEventListener("click", this.handleClick);
}
}
2. Flattening Objects:
----------------------
Approach
We make a function called flatten object which takes input of an object and returns
an object.
Loop through the object and check the type of the current property:
If it is of type Object and it is not an Array, recursively call the function
again.
Otherwise, store the value in the result.
Return the object.
Example: In this example, we are following the above approach.
// Declare an object
let ob = {
Company: "GeeksforGeeks",
Address: "Noida",
contact: +91-999999999,
mentor: {
HTML: "GFG",
CSS: "GFG",
JavaScript: "GFG"
}
};
console.log(flattenObj(ob));
Example 2:
-------------
const nestedObject = {
user: {
name: "John Doe",
address: {
city: "New York",
zip: "10001",
geo: {
lat: "40.7128",
lng: "-74.0060"
}
},
hobbies: ["reading", "traveling"]
}
};
Form Handling: Flattening nested data for easier mapping to form inputs.
API Integration: Converting nested JSON for endpoints that don't accept nested
structures.
Database Storage: Storing flattened keys in NoSQL databases like MongoDB.
output:
[1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]
CASE: 2
Input:
arr = [1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]
n =1
output:
[1,2,3,4,5,6,7,8,[9,10,11],12,13,14,15]
CASE: 3
Input:
arr = [1,2,3,[4,5,6], [7,8, [9,10,11],12], [13,14,15]]
n =2
output:
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
WE will solve this without built in method, and use recursion instead.
var nestedObject = {
data: {
info: {
stuff: {
thing: {
moreStuff: {
magicNumber: 44,
something: "foo2",
random: [4.5],
},
},
stuffValue: 36,
},
},
},
anotherData: [{ arrayval: { testval: [60] } }, 8],
};
return true;
}
// Base case: if obj is equal to target value (including arrays), return true
if (isEqual(obj, value)) {
return true;
}
-----------------------------------------------------------------------------------
----------------------------------------------
Debouncing:
-----------
Developing search bar , auto suggestion- for ex , I searched latop
api will fetch related to it.
Imp thing : It doesnt make api call for each key stroke events
//Debouncing in html
<html>
<body>
<input type="text" onkeyup="getData()"/>
<script src="./js/index.js"></script>
</body>
</html>
//index.ts
let counter = 0;
so it calls 4 time, we want once I pause typing, then api should call.
better function
---------------
//doSomeMagic is debounce only...
//replaces doSomeMagic with Debounce now
const debounce = function (fn, delay) {
return function ()
{
let timer;
let context = this,
args = arguments;
scrolling
THROTTLING:
---------------
is generally used for performance optimization and rate limiting
the functional call or execution.
//window.addEventListener("resize",expensive);
now, window.addEventListener("resize",betterExpensive);
//on every scrolling function expensive will call which
is not correct.
const betterExpensive =
throttle (expensive,limit);
//Roadside discussion:
-----------------------
Question 1:
-----------
Create a button UI and add debounce as follows =>
show Button pressed <X> Times very time button is pressed.
Increase "Triggered <Y> times" count after 800ms of
debounce.
index.html:
----------
<html>
<body>
<button class="increment_btn">Increment</button>
<span class="increment_pressed">0</span>
<span class="increment_count">0</span>
<script src="script.js"></script>
</body>
</html>
script.ts
-----------
var pressedCount = 0;
var triggerCount = 0;
btn.addEventListener('click',()=> {
btnPress.innerHTML = ++pressedCount;
debounceCount();
})
//Throttle:
----------
-----------
const btn = document.querySelector(".increment_btn")'
const btnPress = document.querySelector(".increment_pressed")'
const count = document.querySelector(".increment_count")'
var pressedCount = 0;
var triggerCount = 0;
btn.addEventListener('click',()=> {
btnPress.innerHTML = ++pressedCount;
throttleCount();
})
example 1:
----------
function testFunction(...args) {
const numbers = []; // Array to store all intermediate numbers
return curryFunc;
}
// Usage examples:
console.log(testFunction(1)(2)(3)(4)(5)(6)(7)('add')); // Outputs: 28
console.log(testFunction(1)(2)(3)('multiply')); // Outputs: 6
Currying:
------------
Currying is a technique where a function that takes multiple arguments is
transformed into a series of functions that take one argument each. It enables
partial function application and enhances code flexibility.
Example: Let's create a curried function to calculate the total price of items with
tax.
//Asynchronous
---------------
function asyncForEach(array, cb){
array.forEach(function () {
setTimeout(cb, 0);
})
}
asyncForEach([1,2,3,4], function(i) {
console.log("processing");
delay();
})
JavaScript Runtime itself like V8 which is Runtime inside Chrome.
I have attached one screeshot in document folder.
function foo()
{
return foo();
}
foo();
javascript
Copy code
console.log("Start");
setTimeout(() => {
console.log("Timeout callback");
}, 0);
Promise.resolve().then(() => {
console.log("Promise resolved");
});
console.log("End");
Dry Run and Event Loop Explanation
Execution Context and Call Stack: JavaScript runs code in a single-threaded
environment. It has a call stack that processes function execution one at a time.
Web APIs and Event Queue: When asynchronous tasks like setTimeout or Promise are
encountered, they interact with Web APIs or the Promise microtask queue. These
tasks are processed based on their respective priorities.
Microtask Queue vs. Macrotask Queue:
Output: Start
console.log is synchronous, so it executes immediately.
Step 2: setTimeout(() => { console.log("Timeout callback"); }, 0);
A macrotask is scheduled in the Event Queue with a delay of 0ms. This means it will
execute as soon as the main thread is free and all microtasks are cleared.
Step 3: Promise.resolve().then(() => { console.log("Promise resolved"); });
A microtask is scheduled in the Microtask Queue. It will execute after the current
synchronous code finishes but before macrotasks.
Step 4: console.log("End");
Output: End
This is synchronous and executes immediately.
Step 5: The event loop now processes the Microtask Queue before the Event Queue.
question 2:
-----------
The JavaScript event loop manages task execution in a systematic way to prioritize
and ensure smooth, non-blocking operation. The priority is resolved based on the
type of task and the queue it belongs to. JavaScript categorizes tasks into
synchronous and asynchronous, and asynchronous tasks are further divided into
microtasks and macrotasks.
1. Call Stack
The call stack is where all synchronous code is executed. The event loop starts by
processing all the synchronous code until the call stack is empty. Only then does
it move to handle asynchronous tasks.
2. Task Queues
Microtasks Queue
Microtasks are the highest-priority asynchronous tasks.
Examples: Promise.then, MutationObserver, queueMicrotask
These tasks are executed immediately after the current execution context finishes
but before any macrotasks.
Microtasks can generate additional microtasks, which are processed until the queue
is empty.
Macrotasks Queue
Macrotasks (or tasks) have a lower priority compared to microtasks.
Examples: setTimeout, setInterval, setImmediate (Node.js), I/O tasks, UI rendering
tasks
These tasks are executed after the call stack is cleared and the microtasks queue
is empty.
3. How Priority is Resolved
When the event loop decides what to execute next, it follows these steps:
Start with the main execution context (e.g., global code or function calls).
Process Microtasks:
After clearing the call stack, the event loop processes all microtasks in the
microtasks queue before checking for macrotasks.
Process Macrotasks:
Once the microtasks queue is empty, the event loop picks a task from the macrotasks
queue to execute.
Only one macrotask is picked and executed, after which the event loop returns to
process the microtasks queue again.
Repeat:
The event loop alternates between processing microtasks and macrotasks, ensuring
the microtasks queue is cleared before moving on to the next macrotask.
Example: Task Priority in Action
javascript
Copy code
console.log('Start');
setTimeout(() => {
console.log('Macrotask 1');
}, 0);
Promise.resolve().then(() => {
console.log('Microtask 1');
}).then(() => {
console.log('Microtask 2');
});
setTimeout(() => {
console.log('Macrotask 2');
}, 0);
console.log('End');
Execution Breakdown:
Synchronous Code:
Two setTimeout callbacks are scheduled in the macrotasks queue (Macrotask 1 and
Macrotask 2).
Event Loop Steps:
Question : 3:
---------------
how the event loop manages asynchronous tasks and the execution order of different
tasks such as Promises and setTimeout. This concept is crucial for understanding
how JavaScript handles concurrency.
Ans:
JavaScript uses the event loop to manage asynchronous tasks and maintain
concurrency. It achieves this by handling tasks in distinct phases and prioritizing
them based on their type (e.g., Promises, setTimeout) and the queue they belong to.
Below is a detailed explanation of how the event loop manages asynchronous tasks
and determines the execution order.
Core Concepts
Call Stack:
Microtasks Queue:
Holds high-priority asynchronous tasks.
Includes Promise.then, MutationObserver, and queueMicrotask.
Microtasks are executed after the current execution context finishes and before
moving to macrotasks.
Macrotasks Queue:
Holds lower-priority asynchronous tasks.
Includes setTimeout, setInterval, setImmediate (Node.js), I/O tasks, and rendering
tasks.
Only one macrotask is executed per iteration of the event loop, after all
microtasks are processed.
Event Loop:
The first macrotask in the queue is executed after the microtasks queue is cleared.
After the macrotask executes, the event loop checks the microtasks queue again
before processing the next macrotask.
Example: Promises vs. setTimeout
javascript
Copy code
console.log("Start");
setTimeout(() => {
console.log("Macrotask: setTimeout");
}, 0);
Promise.resolve().then(() => {
console.log("Microtask: Promise 1");
}).then(() => {
console.log("Microtask: Promise 2");
});
console.log("End");
Execution Steps:
Synchronous Code:
The event loop processes the macrotask from setTimeout → Output: Macrotask:
setTimeout.
Output
text
Copy code
Start
End
Microtask: Promise 1
Microtask: Promise 2
Macrotask: setTimeout
Key Points to Understand Execution Order
Synchronous Tasks Always Execute First:
Microtasks like Promises are executed immediately after synchronous code, before
any macrotask.
Macrotasks Are Processed Sequentially:
Only one macrotask is executed per event loop iteration, and then the loop checks
for new microtasks.
Nested Microtasks:
A microtask can queue more microtasks, and these are executed before moving to the
next macrotask.
Visualizing the Event Loop Workflow
Call Stack:
setTimeout(() => {
console.log("Macrotask 1");
Promise.resolve().then(() => {
console.log("Microtask inside Macrotask 1");
});
}, 0);
Promise.resolve().then(() => {
console.log("Microtask 1");
});
setTimeout(() => {
console.log("Macrotask 2");
}, 0);
console.log("End");
Execution Breakdown:
Synchronous:
javascript
Copy code
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
The inner function forms a closure over the lexical environment of outer.
Even though outer has finished execution, the memory allocated for its variables
remains because the inner function still has access to it.
This behavior is enabled by the JavaScript engine's execution context model, which
ensures closures retain their parent scope's environment.
3. Under-the-Hood Mechanism
Heap Allocation for Lexical Environment:
Each function has access to its scope chain, which consists of its own environment
and references to outer environments.
When a variable is accessed, the engine looks up the scope chain to find it.
Garbage Collection:
javascript
Copy code
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
}
};
}
createCounter is executed:
Both increment and decrement share the same lexical environment of createCounter.
Memory management:
plaintext
Copy code
Global Scope
↳ createCounter() Lexical Environment
↳ count: 0
↳ Functions: increment, decrement
Key Points to Remember
Closures enable encapsulation: Variables in closures are private to the function,
providing a mechanism for data hiding.
Performance considerations: Closures can lead to memory leaks if not managed
carefully, as they prevent garbage collection of unused variables.
Practical usage: Closures are used in callbacks, event handlers, currying, and
factory functions.
By understanding the lexical environment and the scope chain, you can write more
effective and efficient JavaScript code.
-----------------------------------------------------------------------------------
----------------------------------------------------
https://roadsidecoder.hashnode.dev/javascript-interview-questions-on-var-let-and-
const
CRP:
------
When a web page loads in a browser, it involves multiple steps in rendering the UI.
The placement and behavior of <script> tags with async and defer attributes
influence the sequence of these steps.
The browser starts parsing the HTML document sequentially, constructing the
Document Object Model (DOM).
If a <script> tag is encountered without async or defer, the browser halts HTML
parsing to download and execute the script.
External Resource Loading
Resources like images, stylesheets, and JavaScript are fetched based on their
placement in the document.
CSSOM Construction
Stylesheets are parsed, and the CSS Object Model (CSSOM) is built alongside the
DOM.
Rendering Tree Construction
The DOM and CSSOM are combined to create the rendering tree, which represents the
visual elements on the page.
Layout and Painting
The browser calculates the layout of elements and paints them on the screen.
Behavior of <script> Tags
Without async or defer
The browser halts HTML parsing to download and execute the script immediately.
This can delay the rendering of the page because parsing and rendering are paused.
html
Copy code
<script src="script.js"></script>
With async
With defer
You said:
please explain in detail with example
ChatGPT said:
ChatGPT
Let’s dive deeper into how async and defer impact the rendering lifecycle of a web
page with detailed explanations and examples.
Lifecycle Recap
When a browser processes an HTML document:
Blocking behavior: The browser pauses rendering while fetching and executing each
script.
Slow page load time.
2. Using async
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example: async</title>
<script src="script1.js" async></script>
<script src="script2.js" async></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Behavior:
Both script1.js and script2.js are downloaded asynchronously in parallel while the
HTML is parsed.
As soon as a script is ready, it is executed. The order of execution is not
guaranteed.
Scenario:
Both script1.js and script2.js are downloaded asynchronously in parallel while the
HTML is parsed.
Scripts are executed in the order they appear in the document after the DOM is
fully parsed.
Scenario:
Scripts execute later, so they can’t run before the DOM is ready.
Visual Timeline of Events
No async or defer:
css
Copy code
[ Parse HTML ] ----> [ Download script1.js ] ----> [ Execute script1.js ] ---->
[ Parse HTML ] ...
With async:
css
Copy code
[ Parse HTML ] -- [ Download script1.js ] --> [ Execute script1.js ] --- [ Parse
HTML ] -- ...
[ Download script2.js ] --> [ Execute script2.js ]
With defer:
css
Copy code
[ Parse HTML ] -- [ Download script1.js ] [ Download script2.js ] --> [ Execute
script1.js ] --> [ Execute script2.js ]
Real-World Example
Imagine you have these files:
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Real-World Example</title>
<!-- Analytics script with async -->
<script src="analytics.js" async></script>
<!-- Main script with defer -->
<script src="main.js" defer></script>
</head>
<body>
<h1>Hello World</h1>
<button id="myButton">Click Me</button>
</body>
</html>
Explanation:
analytics.js:
Downloads asynchronously and executes as soon as it’s ready, without waiting for
the DOM.
Doesn’t depend on DOM structure, so async is appropriate.
main.js:
Downloads asynchronously but executes after the DOM is fully parsed.
Ensures DOM is ready before attaching event listeners.
Best Practices
Use async for scripts like analytics or third-party libraries that don’t interact
with the DOM.
Use defer for scripts that manipulate or rely on the DOM.
Avoid inline scripts or scripts without attributes unless necessary, as they block
rendering.
By understanding and using async and defer appropriately, you can improve the
performance and user experience of your web pages.
You said:
critical rendering path
ChatGPT said:
ChatGPT
The Critical Rendering Path (CRP) refers to the sequence of steps a browser takes
to convert HTML, CSS, and JavaScript into pixels on the user's screen. Optimizing
the CRP is crucial for improving page load performance and reducing time to the
first meaningful paint.
The browser starts parsing the HTML document from top to bottom.
It constructs the Document Object Model (DOM), a tree-like representation of the
HTML structure.
CSS Parsing (Building the CSSOM)
When the browser encounters external or inline CSS, it downloads and parses these
styles to build the CSS Object Model (CSSOM).
Until the CSSOM is complete, rendering is blocked.
JavaScript Execution
The browser calculates the position and size of each element in the Render Tree
based on the viewport size and CSS rules.
This process is called layout or reflow.
Painting
The browser paints pixels to the screen based on the layout and styling
information.
Detailed Flow of the CRP
Here’s how the CRP works in a real-world scenario:
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<script src="script.js" defer></script>
<title>Critical Rendering Path Example</title>
</head>
<body>
<h1>Hello World</h1>
<p>This is a demo of the Critical Rendering Path.</p>
</body>
</html>
Step-by-Step Breakdown:
HTML Parsing Starts:
When the <link> tag for styles.css is encountered, the browser pauses rendering to
download and parse the stylesheet.
CSSOM is constructed.
JavaScript Deferred:
The script.js with the defer attribute is downloaded in parallel but executed only
after the DOM is fully built.
Render Tree Built:
Once the DOM and CSSOM are ready, the browser constructs the Render Tree.
Layout and Paint:
The browser calculates the positions of visible elements and paints them to the
screen.
Optimization Techniques
1. Minimize Render-Blocking Resources
CSS and JavaScript block rendering. Minimize or defer them:
Use defer or async for JavaScript.
Inline critical CSS to avoid blocking.
Minify and compress resources.
2. Critical CSS
Extract and inline only the CSS required for above-the-fold content.
Defer non-critical CSS.
3. Reduce HTML Complexity
Minimize the size and complexity of your HTML to speed up DOM construction.
4. Preload Key Resources
Use <link rel="preload"> to prioritize loading critical resources.
5. Lazy Load Non-Critical Resources
Defer loading of images, videos, and other heavy assets until they are needed.
6. Use Efficient Fonts
Optimize font loading with font-display: swap to avoid rendering delays caused by
font loading.
Visualizing the CRP
Timeline:
HTML is parsed to build the DOM.
CSS is fetched, parsed, and combined with the DOM to create the Render Tree.
JavaScript modifies the DOM/CSSOM (if necessary).
Layout and paint are completed.
Blockers:
CSS is a render-blocking resource. Rendering cannot proceed until the CSSOM is
complete.
JavaScript can block DOM construction if not deferred or asynchronous.
Critical Rendering Path Example with Optimization
Optimized HTML:
html
Copy code
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Inline critical CSS -->
<style>
body { font-family: Arial, sans-serif; margin: 0; }
h1 { color: darkblue; }
</style>
<!-- Preload key assets -->
<link rel="preload" href="styles.css" as="style">
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
CRP:
----
https://www.youtube.com/watch?v=Tnp3yX9Z93Q&t=12s
console.log('Start')
withAsyncAwait();
Start is logged in the console.
when the code execution comes at the await keyword. The withAsyncAwait function is
pushed out of the call stack.
At this point, if there is anything else to execute other than the withAsyncAwait
function, that will be executed. If nothing else is present to execute, then the
Call Stack would be EMPTY.
The main JS execution thread is never blocked.
Once the promise is resolved after 10 seconds, the withAsyncAwait function is
pushed back inside the call stack and starts to execute where it left off.
So first, the value of the resolved promise is stored in variable data, and it is
logged in the console.
After that, the next console statement is run.
Without the above knowledge, it looks like the JS code is blocked. but, after
seeing how it actually works in the Call Stack will make it clear that JS code is
never blocked. The call stack is never held back.