0
0
Javascriptprogramming~15 mins

Creating promises in Javascript - Mechanics & Internals

Choose your learning style9 modes available
Overview - Creating promises
What is it?
A promise in JavaScript is a special object that represents a task that will finish in the future. It can either succeed with a result or fail with an error. Creating a promise means writing code that sets up this future task and tells JavaScript what to do when it finishes or fails. This helps manage actions that take time, like loading data from the internet.
Why it matters
Without promises, handling tasks that take time would be messy and confusing, often leading to deeply nested code that is hard to read and maintain. Promises let us write cleaner, easier-to-understand code that waits for tasks to finish before moving on. This makes programs more reliable and user-friendly, especially when dealing with slow operations like fetching data or reading files.
Where it fits
Before learning how to create promises, you should understand basic JavaScript functions and callbacks. After mastering promises, you can learn about async/await syntax, which builds on promises to make asynchronous code even simpler and more readable.
Mental Model
Core Idea
A promise is like a sealed envelope that will eventually contain a result or an error, and creating a promise means preparing that envelope and deciding what to do when it is opened.
Think of it like...
Imagine ordering a package online. You place the order (create the promise), and you don’t get the package immediately. Later, the package either arrives successfully or you get a notification that it was lost. You decide in advance what to do when the package arrives or if it gets lost.
┌───────────────┐
│ Create Promise│
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Pending State │
│ (waiting...)  │
└──────┬────────┘
       │
  ┌────┴─────┐
  │          │
  ▼          ▼
┌────────┐ ┌────────┐
│Fulfilled││Rejected│
│ (done) ││ (error)│
└────────┘└────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding asynchronous tasks
🤔
Concept: Before creating promises, you need to understand that some tasks take time and don’t finish immediately.
In JavaScript, some actions like loading images or fetching data from the internet happen asynchronously. This means the program doesn’t wait for them to finish before moving on. Instead, it continues running other code.
Result
You realize that some tasks need special handling to know when they finish.
Understanding asynchronous tasks is essential because promises are designed to handle these delayed results cleanly.
2
FoundationWhat is a promise object
🤔
Concept: A promise is an object that represents a future result of an asynchronous task.
A promise can be in one of three states: pending (waiting), fulfilled (success), or rejected (error). It lets you attach actions to run when the task finishes or fails.
Result
You know that a promise is a placeholder for a future value or error.
Knowing the states of a promise helps you understand how to react to asynchronous results.
3
IntermediateCreating a basic promise
🤔Before reading on: do you think creating a promise immediately runs the task inside it or waits until you use it? Commit to your answer.
Concept: You learn how to write code that creates a promise and starts an asynchronous task inside it.
Use the Promise constructor with a function that takes two arguments: resolve and reject. Inside, run your task and call resolve(value) if successful or reject(error) if it fails. Example: const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Task done'); }, 1000); });
Result
A promise object is created and the task inside starts immediately.
Understanding that the promise executor runs right away helps avoid confusion about when tasks start.
4
IntermediateHandling success and failure
🤔Before reading on: do you think a promise can only succeed or only fail, or can it do both at different times? Commit to your answer.
Concept: Learn how to attach functions to handle the promise’s success or failure using then and catch methods.
You use .then() to specify what to do when the promise fulfills and .catch() for when it rejects. Example: myPromise.then(result => { console.log('Success:', result); }).catch(error => { console.error('Error:', error); });
Result
Your program reacts properly when the task finishes or fails.
Knowing how to handle both outcomes prevents your program from crashing or hanging.
5
IntermediateCreating promises with conditions
🤔Before reading on: do you think you can decide inside a promise whether to resolve or reject based on some condition? Commit to your answer.
Concept: You learn to create promises that resolve or reject depending on logic inside the executor function.
Inside the promise, check conditions and call resolve or reject accordingly. Example: const checkNumber = (num) => new Promise((resolve, reject) => { if (num > 10) { resolve('Number is big'); } else { reject('Number is small'); } });
Result
Promises can represent tasks that succeed or fail based on real conditions.
Understanding conditional resolution lets you model real-world decisions asynchronously.
6
AdvancedPromise executor runs immediately
🤔Before reading on: do you think the code inside a promise runs only when you call then, or right when you create the promise? Commit to your answer.
Concept: The function passed to new Promise runs immediately, not lazily when you use then or catch.
When you create a promise, its executor function runs at once. This means any code inside starts running immediately, even if you don’t attach handlers yet. Example: const p = new Promise((resolve) => { console.log('Executor runs'); resolve('Done'); }); // Logs 'Executor runs' immediately
Result
You see that creating a promise triggers its task right away.
Knowing this prevents bugs where you expect the task to start only after attaching handlers.
7
ExpertAvoiding common promise pitfalls
🤔Before reading on: do you think returning a promise inside another promise’s executor is a good practice or a bad practice? Commit to your answer.
Concept: Learn why returning promises inside executors is discouraged and how to chain promises properly instead.
Inside a promise executor, returning another promise does nothing special. Instead, you should chain promises using then outside the executor. Bad example: new Promise((resolve) => { return fetch('url'); // ignored }); Good example: fetch('url').then(response => { // handle response });
Result
You avoid confusing code that doesn’t behave as expected.
Understanding promise chaining and executor behavior helps write clean, bug-free asynchronous code.
Under the Hood
When you create a promise, JavaScript sets up an internal object with a state (pending, fulfilled, or rejected). The executor function runs immediately and can call resolve or reject to change the state. When the state changes, JavaScript queues the attached handlers (then, catch) to run asynchronously after the current code finishes. This ensures consistent timing and avoids blocking the main thread.
Why designed this way?
Promises were designed to replace callback hell by providing a clear, chainable way to handle asynchronous results. Running the executor immediately ensures tasks start right away, avoiding surprises. The asynchronous queuing of handlers prevents race conditions and keeps the event loop smooth. Alternatives like callbacks were error-prone and hard to read, so promises offer a structured, reliable pattern.
┌───────────────┐
│ Promise Object│
│  State: PENDING│
└──────┬────────┘
       │ Executor runs immediately
       ▼
┌───────────────┐
│ Executor Code │
│ Calls resolve │
│ or reject    │
└──────┬────────┘
       │ Changes state
       ▼
┌───────────────┐
│ State: FULFILLED│
│ or REJECTED   │
└──────┬────────┘
       │ Queue handlers
       ▼
┌───────────────┐
│ then/catch run│
│ asynchronously│
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does a promise start running only after you call then, or immediately when created? Commit to your answer.
Common Belief:Promises only start their task when you attach a then handler.
Tap to reveal reality
Reality:The executor function inside a promise runs immediately upon creation, regardless of then or catch.
Why it matters:Believing this causes bugs where tasks start too early or too late, leading to unexpected behavior.
Quick: Can a promise change from fulfilled back to pending or rejected? Commit to your answer.
Common Belief:A promise can change its state multiple times during its life.
Tap to reveal reality
Reality:A promise’s state is immutable once it changes from pending to fulfilled or rejected.
Why it matters:Expecting multiple state changes can cause logic errors and incorrect assumptions about program flow.
Quick: Does returning a promise inside another promise’s executor chain them automatically? Commit to your answer.
Common Belief:Returning a promise inside a promise executor chains them automatically.
Tap to reveal reality
Reality:Returning a promise inside the executor does nothing special; chaining must be done outside with then.
Why it matters:Misunderstanding this leads to broken promise chains and unhandled asynchronous results.
Quick: Can a promise resolve with another promise as its value? Commit to your answer.
Common Belief:A promise can only resolve with simple values, not other promises.
Tap to reveal reality
Reality:Promises can resolve with other promises, which causes them to adopt the inner promise’s state.
Why it matters:Knowing this helps build complex asynchronous flows and avoid nested callbacks.
Expert Zone
1
Promises always run their executor immediately, but handlers attached with then run asynchronously, ensuring predictable timing.
2
A promise’s resolve function can only be called once; subsequent calls are ignored, preventing multiple state changes.
3
Promises can be chained infinitely, and returning a value inside then creates a new promise resolved with that value.
When NOT to use
Promises are not ideal for cancelable tasks or streams of data. For those, use AbortController for cancellation or Observables for multiple values over time.
Production Patterns
In real-world apps, promises are often created to wrap legacy callback APIs, enabling modern async/await usage. They are also used to coordinate multiple asynchronous tasks with Promise.all or Promise.race for efficient parallel processing.
Connections
Async/Await
Builds-on
Understanding how promises work is essential to using async/await, which simplifies promise handling into readable synchronous-style code.
Event Loop
Underlying mechanism
Promises rely on the event loop to schedule their handlers asynchronously, so knowing the event loop helps understand promise timing and behavior.
Futures in concurrent programming
Same pattern in different domain
Promises in JavaScript are similar to futures in other languages, representing a value that will be available later, showing a common solution to asynchronous problems across programming.
Common Pitfalls
#1Creating a promise but forgetting to call resolve or reject inside the executor.
Wrong approach:const p = new Promise((resolve, reject) => { // forgot to call resolve or reject });
Correct approach:const p = new Promise((resolve, reject) => { resolve('Done'); });
Root cause:Not understanding that the executor must explicitly signal completion or failure.
#2Returning a promise inside the executor expecting automatic chaining.
Wrong approach:const p = new Promise((resolve) => { return fetch('url'); });
Correct approach:const p = fetch('url'); p.then(response => { /* handle */ });
Root cause:Misunderstanding that promise chaining happens outside the executor function.
#3Attaching then handlers after the promise has already resolved, expecting the executor to run again.
Wrong approach:const p = new Promise((resolve) => { resolve('Done'); }); // later p.then(result => console.log(result));
Correct approach:const p = new Promise((resolve) => { resolve('Done'); }); p.then(result => console.log(result));
Root cause:Not realizing that the executor runs once immediately and then handlers run asynchronously, even if attached later.
Key Takeaways
Promises represent future results of asynchronous tasks and have three states: pending, fulfilled, or rejected.
Creating a promise runs its executor function immediately, starting the task right away.
You handle success and failure by attaching then and catch handlers, which run asynchronously after the task finishes.
Promises can be chained and combined to manage complex asynchronous flows cleanly.
Understanding promises deeply helps write reliable, readable asynchronous JavaScript code and prepares you for async/await.