0
0
Javascriptprogramming~15 mins

Why promises are used in Javascript - Why It Works This Way

Choose your learning style9 modes available
Overview - Why promises are used
What is it?
Promises in JavaScript are objects that represent the eventual result of an asynchronous operation. They allow you to write code that waits for something to finish, like loading data, without stopping everything else. Instead of blocking the program, promises let you handle success or failure once the operation completes. This makes managing tasks that take time easier and cleaner.
Why it matters
Without promises, handling tasks that take time, like fetching data from the internet, would be messy and confusing. Programs might freeze or become hard to read because they wait for things to finish before moving on. Promises solve this by letting the program keep running and respond when the task is done, improving user experience and code clarity.
Where it fits
Before learning promises, you should understand basic JavaScript functions and callbacks, which are older ways to handle waiting for tasks. After promises, you can learn async/await syntax, which builds on promises to make asynchronous code look even simpler and more like regular code.
Mental Model
Core Idea
A promise is a placeholder for a value that will be available later, letting your program keep working while waiting.
Think of it like...
Imagine ordering a pizza by phone. You hang up and do other things while waiting. The promise is like the pizza delivery: you don’t have it now, but you know it will arrive. When it does, you can eat it or handle problems if the delivery fails.
┌───────────────┐
│   Promise     │
│ (waiting for) │
│  result later │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│  Success:     │
│  use the data │
└───────────────┘
       or
┌───────────────┐
│  Failure:     │
│  handle error │
└───────────────┘
Build-Up - 6 Steps
1
FoundationUnderstanding Asynchronous Tasks
🤔
Concept: Introduce the idea that some tasks take time and don't finish immediately.
In JavaScript, some actions like loading images or fetching data from the internet don't happen instantly. Instead of waiting and stopping everything, JavaScript lets these tasks run in the background. This is called asynchronous behavior.
Result
You learn that JavaScript can do other things while waiting for slow tasks to finish.
Understanding asynchronous tasks is key because it explains why we need special tools like promises to handle waiting without freezing the program.
2
FoundationCallbacks: The Old Way to Wait
🤔
Concept: Show how functions called later (callbacks) handle asynchronous results.
A callback is a function you give to another function to run later, after a task finishes. For example, you ask for data and provide a callback to process it when ready. But callbacks can get messy when many tasks depend on each other.
Result
You see how callbacks let you handle asynchronous results but can lead to complicated code.
Knowing callbacks helps you appreciate why promises were created to make asynchronous code cleaner and easier to manage.
3
IntermediatePromises: A Cleaner Way to Handle Waiting
🤔Before reading on: do you think promises immediately have the result or wait for it? Commit to your answer.
Concept: Introduce promises as objects that represent future results, improving code readability and error handling.
A promise starts in a 'pending' state, then becomes 'fulfilled' with a result or 'rejected' with an error. You use .then() to handle success and .catch() for errors. This avoids deeply nested callbacks and makes code easier to follow.
Result
You can write asynchronous code that looks more like normal, step-by-step code.
Understanding promises as placeholders for future results helps you write clearer and more maintainable asynchronous code.
4
IntermediateChaining Promises for Sequential Tasks
🤔Before reading on: do you think you can run multiple promises one after another easily? Commit to yes or no.
Concept: Show how promises can be linked to run tasks in order without nesting callbacks.
You can return a new promise inside a .then() to chain tasks. Each step waits for the previous one to finish, making sequences easy to read and write.
Result
You can perform multiple asynchronous steps in a clean, readable way.
Knowing how to chain promises prevents 'callback hell' and keeps your code organized.
5
AdvancedHandling Multiple Promises Together
🤔Before reading on: do you think promises can wait for several tasks at once? Commit to yes or no.
Concept: Introduce Promise.all and Promise.race to manage multiple promises simultaneously.
Promise.all waits for all promises to finish and gives all results together. Promise.race waits for the first promise to finish, ignoring the rest. These help when you want to run tasks in parallel and react accordingly.
Result
You can efficiently manage multiple asynchronous operations at the same time.
Understanding these methods lets you optimize performance and control flow in complex asynchronous scenarios.
6
ExpertPromises Internals and Microtask Queue
🤔Before reading on: do you think promise callbacks run immediately or after current code finishes? Commit to your answer.
Concept: Explain how promises use the JavaScript event loop and microtask queue to schedule callbacks.
When a promise settles, its .then() or .catch() callbacks are placed in the microtask queue, which runs after the current code but before rendering or other tasks. This ensures predictable timing and avoids blocking.
Result
You understand why promise callbacks run asynchronously but soon after the current code.
Knowing the event loop and microtask queue behavior helps prevent subtle bugs and performance issues in asynchronous code.
Under the Hood
Promises are objects that track the state of an asynchronous operation: pending, fulfilled, or rejected. When the operation completes, the promise changes state and schedules its callbacks to run in the microtask queue. This queue runs after the current script but before other tasks, ensuring callbacks execute in a predictable order without blocking the main thread.
Why designed this way?
Promises were designed to solve callback hell and improve error handling in asynchronous JavaScript. The microtask queue ensures promise callbacks run quickly after the current code, avoiding delays and race conditions. Alternatives like callbacks were harder to manage and led to deeply nested code and error handling challenges.
┌───────────────┐
│ Async Task    │
│ (e.g., fetch) │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Promise State │
│ Pending →     │
│ Fulfilled or  │
│ Rejected      │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Microtask     │
│ Queue (then)  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Callback Run  │
│ (handle data) │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do promises block the program until they finish? Commit to yes or no.
Common Belief:Promises pause the program and wait for the result before moving on.
Tap to reveal reality
Reality:Promises do not block; they let the program continue running and handle results later.
Why it matters:Believing promises block leads to confusion about program flow and can cause incorrect assumptions about performance.
Quick: Does a promise always run its .then() callback immediately after creation? Commit to yes or no.
Common Belief:The .then() callback runs right away when attached to a promise.
Tap to reveal reality
Reality:The .then() callback runs asynchronously after the current code finishes, via the microtask queue.
Why it matters:Misunderstanding this timing can cause bugs where code runs in an unexpected order.
Quick: Can a promise change from fulfilled back to pending or rejected? Commit to yes or no.
Common Belief:A promise can change its state multiple times as the operation progresses.
Tap to reveal reality
Reality:A promise's state is final once fulfilled or rejected; it cannot change again.
Why it matters:Expecting multiple state changes can cause logic errors and improper handling of asynchronous results.
Quick: Does Promise.all return results as soon as any promise finishes? Commit to yes or no.
Common Belief:Promise.all returns results immediately when the first promise completes.
Tap to reveal reality
Reality:Promise.all waits for all promises to finish successfully or rejects if any fail.
Why it matters:Misusing Promise.all can cause unexpected delays or missed errors in concurrent tasks.
Expert Zone
1
Promises always run their callbacks asynchronously, even if already resolved, ensuring consistent timing.
2
Chaining promises returns new promises, allowing flexible composition but requiring careful error propagation.
3
Unhandled promise rejections can cause silent failures; modern environments warn or crash to encourage proper handling.
When NOT to use
Promises are not ideal for cancelable operations or streaming data. In such cases, use AbortController for cancellation or Observables for streams, which provide more control and flexibility.
Production Patterns
In real-world apps, promises are combined with async/await for clearer syntax. Developers use Promise.allSettled to handle multiple tasks without failing early, and wrap promises with timeout logic to avoid hanging operations.
Connections
Event Loop
Promises rely on the event loop's microtask queue to schedule callbacks.
Understanding the event loop clarifies why promise callbacks run asynchronously and helps debug timing issues.
Futures in Other Languages
Promises in JavaScript are similar to futures in languages like Java or Dart, representing values available later.
Knowing this connection helps grasp asynchronous programming concepts across different languages.
Project Management Deadlines
Promises are like setting deadlines for tasks in a project, where you plan to check results later without stopping other work.
This cross-domain link shows how managing future outcomes efficiently is a universal challenge, not just in programming.
Common Pitfalls
#1Ignoring errors in promises leads to silent failures.
Wrong approach:fetch(url).then(response => response.json());
Correct approach:fetch(url).then(response => response.json()).catch(error => console.error(error));
Root cause:Beginners often forget to handle promise rejections, causing errors to go unnoticed.
#2Nesting .then() calls creates callback hell again.
Wrong approach:doTask1().then(result1 => { doTask2().then(result2 => { console.log(result2); }); });
Correct approach:doTask1().then(result1 => doTask2()).then(result2 => console.log(result2));
Root cause:Not returning promises inside .then() leads to nested callbacks and harder-to-read code.
#3Assuming promise callbacks run immediately causes timing bugs.
Wrong approach:let x = 0; Promise.resolve().then(() => { x = 1; }); console.log(x);
Correct approach:let x = 0; Promise.resolve().then(() => { x = 1; }); setTimeout(() => console.log(x), 0);
Root cause:Misunderstanding that promise callbacks run after current code finishes leads to unexpected output.
Key Takeaways
Promises represent values that will be available in the future, letting programs handle asynchronous tasks cleanly.
They prevent blocking the program by running callbacks after the current code, improving user experience.
Promises improve on callbacks by avoiding nested code and providing better error handling.
Understanding the event loop and microtask queue is essential to grasp promise behavior and timing.
Using promises correctly avoids common bugs and makes asynchronous JavaScript code easier to write and maintain.