Introduction to Promises: Mastering Async JavaScript Flow
Learn how to use JavaScript promises to manage asynchronous tasks effectively. Master .then() and .catch() to handle network requests and errors with confidence.

Previously in this course, we explored asynchronous JavaScript basics where we used setTimeout to delay code execution. While timers are useful for simple delays, they don't help us manage the outcome of a task—like fetching data from a server.
In modern web development, "doing things that take time" is the norm. Whether you are reading a file, querying a database, or calling a weather API, your code needs a way to track the status of that operation. This is where the Promise object comes in.
What is a Promise?
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation. Think of it like ordering a coffee at a busy cafe:
- Pending: You place your order and receive a buzzer. The coffee isn't ready, but you have a "promise" that it will be.
- Fulfilled (Resolved): The buzzer goes off, and you get your drink. The operation succeeded.
- Rejected: The barista tells you they ran out of milk. The operation failed.
In JavaScript, a Promise allows us to attach code that runs only after the result is known, keeping our main program logic clean and readable.
Using .then() for Success

When a Promise resolves successfully, we use the .then() method to define what happens next. You can think of .then() as a callback that only triggers once the "buzzer" of your asynchronous task goes off.
Here is a basic example of creating and consuming a Promise:
JAVASCRIPTconst orderCoffee = new Promise((resolve, reject) => { const isCoffeeReady = true; if (isCoffeeReady) { resolve("Here is your latte!"); } else { reject("Sorry, we are out of coffee."); } }); // Handling the success case orderCoffee.then((message) => { console.log(message); // "Here is your latte!" });
The resolve function inside the constructor tells the Promise that it finished successfully, passing the result (the message) to our .then() block.
Handling Errors with .catch()
Real-world network requests often fail—perhaps the user is offline or the server is down. If you don't handle these failures, your application might break. We use the .catch() method to "catch" any errors that happen during the process.
Let’s update our example to simulate a failure:
JAVASCRIPTconst orderCoffee = new Promise((resolve, reject) => { const isCoffeeReady = false; // Simulating a problem if (isCoffeeReady) { resolve("Here is your latte!"); } else { reject("Error: We ran out of milk."); } }); orderCoffee .then((message) => { console.log(message); }) .catch((error) => { console.error(error); // "Error: We ran out of milk." });
By chaining .catch(), you ensure that your code doesn't just crash when something goes wrong. This pattern is essential for handling asynchronous errors and maintaining a smooth user experience.
Hands-on Exercise: The Mock Request
In our project, we’ll eventually fetch weather data. Let’s practice by creating a function that returns a Promise, simulating a network delay.
- Create a function called
fetchWeatherDatathat returns anew Promise. - Inside, use
setTimeoutto wait 2 seconds. - After 2 seconds, randomly decide to either resolve with an object
{ temp: 72 }or reject with an error message. - Call your function and use
.then()to log the temperature and.catch()to log the error.
Common Pitfalls
- Forgetting to return a result: If you don't call
resolve()orreject(), your Promise will stay in the "pending" state forever. - Assuming linear execution: Remember that everything inside
.then()happens after the main code block has finished executing. Don't try to access variables defined inside a.then()from outside of it. - Swallowing errors: Never leave a Promise chain without a
.catch(). If you do, errors will often fail silently or trigger "Unhandled Promise Rejection" warnings in the console, which can make debugging asynchronous stability much harder.
If you find yourself managing multiple concurrent requests, you might eventually need more advanced patterns like Promise.allSettled to ensure one failure doesn't halt your entire dashboard.
FAQ
Q: Can I use multiple .then() blocks?
A: Yes! This is called "chaining." You can return a value from one .then() and it will be passed to the next one in the chain.
Q: Does every Promise need a .catch()? A: Technically no, but you should always include one in production code to prevent silent failures.
Q: What is the difference between resolve and reject?
A: resolve signals success and moves the Promise to the "fulfilled" state; reject signals an error and moves it to the "rejected" state.
Recap
Promises allow us to write cleaner, more manageable asynchronous code. By using new Promise(), we define the task, and by using .then() and .catch(), we define exactly how to react to success and failure. You now have the tools to handle tasks that take time without blocking the rest of your page.
Up next: We will apply these concepts to the real world by learning how to use the Fetch API to pull data from actual servers.


