Promise
A JavaScript Promise is an object that represents the future result of an asynchronous operation.
It is used to handle operations such as fetching data, reading files, or calling an API.
Promises have three states:
- Pending – the initial state, waiting for a result.
- Fulfilled – the operation completed successfully.
- Rejected – the operation failed.
You can create a promise using the Promise constructor:
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Done!"), 1000);
});
promise.then(value => console.log(value)); // Output: Done!
In short: Promises help manage asynchronous tasks without using too many nested callbacks.
Callback
A callback is a function passed as an argument to another function.
It runs after something happens — for example, after an API call or a file is read.
function getData(callback) {
setTimeout(() => {
callback("Data received!");
}, 1000);
}
getData(message => console.log(message));
Callbacks allow asynchronous code, but they can lead to callback hell — nested and hard-to-read code.
That’s why JavaScript introduced Promises and async/await to make async code easier to handle.
CORS
CORS (Cross-Origin Resource Sharing) is a security feature in browsers.
It controls which websites can request data from another domain.
By default, browsers use the Same-Origin Policy, which blocks requests to different domains.
CORS allows exceptions through HTTP headers like:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-Credentials
Example:
A website at example.com can fetch data from api.server.com only if the server allows it via CORS headers.
In short: CORS protects users by limiting which websites can access external resources.
Async vs Sync
Synchronous (Sync) code runs step by step.
The program waits for one task to finish before starting the next.
Asynchronous (Async) code allows the program to continue running while waiting for a task to finish.
This makes the program faster and more responsive.
Example:
console.log("A");
setTimeout(() => {
console.log("B");
}, 1000);
console.log("C");
// Output:
// A
// C
// B
In short: Async code doesn’t block the program; sync code does.
JS Async
JavaScript handles asynchronous operations in three main ways:
- Callbacks – pass a function to run after something happens.
- Promises – objects that represent future results (with
.then()and.catch()). - Async/Await – a simpler syntax that looks like synchronous code.
Example:
async function fetchData() {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
}
fetchData();
In short: Async/await makes async code easier to read and write.
Observables
Both Promises and Observables handle asynchronous operations, but they are different:
| Feature | Promise | Observable |
|---|---|---|
| Execution | One-time | Continuous stream |
| Values | One value | Multiple values over time |
| Start | Eager (starts immediately) | Lazy (starts when subscribed) |
| Cancel | Not possible | Can unsubscribe |
| Operators | Few built-in | Many for transforming data |
Example (RxJS):
import { Observable } from "rxjs";
const observable = new Observable(subscriber => {
subscriber.next("A");
subscriber.next("B");
subscriber.complete();
});
observable.subscribe(value => console.log(value));
In short: Promises handle one result; Observables handle streams of data over time.
Prioritization
JavaScript prioritizes tasks differently depending on their type.
- Synchronous tasks run first (e.g.,
console.log()). - Promises go to the microtask queue (high priority).
- setTimeout tasks go to the task queue (lower priority).
Example:
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");
// Output:
// A
// D
// C
// B
In short: Promises run before setTimeout, even with zero delay.
Event Loop
The event loop allows JavaScript to run asynchronous code smoothly.
It constantly checks the call stack, microtask queue, and task queue.
Basic flow:
- Run synchronous code on the call stack.
- Process all microtasks (like resolved Promises).
- Process one macrotask (like setTimeout).
- Repeat forever.
This process keeps JavaScript non-blocking and responsive.
In short: The event loop manages the order of all async tasks.
Hoisting
Hoisting means JavaScript moves declarations to the top of the scope during compilation.
Example:
console.log(x); // undefined
var x = 5;
Here, the variable x is hoisted, but only the declaration, not the value.
Function declarations are also hoisted:
sayHello(); // "Hello!"
function sayHello() {
console.log("Hello!");
}
Note:
- Only
varand function declarations are hoisted. let,const, and function expressions are not.
In short: Hoisting lets you use variables or functions before declaring them — but it can cause confusion, so declare first.
✅ Summary
- Promises simplify async tasks.
- Callbacks are older but can get messy.
- CORS controls web security.
- Async/Sync defines how code executes.
- Async/Await makes async code clean.
- Observables manage continuous data streams.
- Prioritization explains task order.
- Event Loop controls execution flow.
- Hoisting moves declarations up automatically.