🌟 **Advanced JavaScript**
A closure is a function that "remembers" the scope where it was created, allowing it to access variables from its parent function even after the parent has finished execution.
On this page
🌟 Advanced JavaScript
I. Closure
A closure is a function that "remembers" the scope where it was created, allowing it to access variables from its parent function even after the parent has finished execution.
🌱 Basic Closure Example
function createCounter() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
const counter = createCounter(); // counter is now a closure
counter(); // Prints: 1
counter(); // Prints: 2
counter(); // Prints: 3
// The `count` variable still exists and is remembered across calls.
🛡️ Applications
a. Data Privacy / Encapsulation
Closures enable simulating private variables (before ES6 class #private fields).
function createOrderManager() {
let total = 0;
return {
add(price) {
total += price;
},
getTotal() {
return total;
},
};
}
const order = createOrderManager();
order.add(100);
console.log(order.getTotal()); // 100
With ES6+, you can use class private fields:
class Order {
#total = 0;
add(price) {
this.#total += price;
}
getTotal() {
return this.#total;
}
}
Closures are still useful when:
- Using functional programming
- Creating simple modules, singletons
b. Function Factories
Create functions with pre-configured initial parameters.
function multiplyBy(x) {
return function (y) {
return x * y;
};
}
const double = multiplyBy(2);
console.log(double(5)); // 10
c. Currying
Transform a function with multiple parameters into a sequence of functions, each taking one parameter.
// The original, non-"curried" function
function log(time, level, message) {
console.log(`[${time}] [${level}]: ${message}`);
}
// The "curried" version
function curryLog(time) {
return function (level) {
return function (message) {
console.log(`[${time}] [${level}]: ${message}`);
};
};
}
// Usage:
const logToday = curryLog(new Date().toLocaleDateString()); // Partially applied, creates a log function for today
const logInfoToday = logToday("INFO"); // Applied further, creates an INFO log function for today
logInfoToday("User logged in.");
logInfoToday("User updated their profile.");
II. Promises & Async/Await
1. Promises
A Promise is an object representing a future value (or error), commonly used for handling asynchronous actions, making code more readable and avoiding callback hell.
🌳 3 Promise States
| State | Meaning |
|---|---|
| Pending | Promise just created, not yet completed or failed |
| Fulfilled | The action succeeded and returned a value |
| Rejected | The action failed and returned an error |
const demoPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const isSuccess = true;
if (isSuccess) {
resolve("Success!");
} else {
reject("Failure!");
}
}, 1000);
});
⚡ Handling Promise Results
- .then(onFulfilled, onRejected): Handle success or failure.
- .catch(onRejected): Handle failure.
- .finally(onFinally): Always executed when the Promise is settled (either fulfilled or rejected).
demoPromise
.then((result) => {
console.log("Result:", result); // Success!
})
.catch((error) => {
console.error("Error:", error);
})
.finally(() => {
console.log("Operation complete!");
});
🔗 Promise Chaining
We can chain multiple asynchronous actions by returning a Promise in a .then().
function plus1(x) {
return new Promise((resolve) => {
setTimeout(() => resolve(x + 1), 500);
});
}
plus1(1)
.then((result) => {
console.log(result); // 2
return plus1(result);
})
.then((result) => {
console.log(result); // 3
});
🛠️ Handling Multiple Promises in Parallel
| Method | Description |
|---|---|
Promise.all([p1, p2, ...]) | Waits for all Promises to complete, returns array of results. Any rejected Promise rejects all. |
Promise.race([p1, p2, ...]) | Returns result of the first settled Promise (fulfilled or rejected). |
Promise.allSettled([p1, ...]) | Returns status and value/reason for all Promises, regardless of outcome. |
Promise.any([p1, ...]) | Returns value of the first fulfilled Promise. If all are rejected, returns an AggregateError. |
Promise.all([p1, p2])
.then(([v1, v2]) => {
/*...*/
})
.catch((err) => {
/*...*/
});
Summary:
- The asynchronous flow ensures JS apps don't "freeze" while handling multiple tasks.
- Closures are powerful for data protection, modularization, and functional programming.
- Promises make asynchronous code more readable and maintainable.
