JavaScript Promises in Modern Development: Why They Still Matter
Are JavaScript Promises Still Necessary in Modern Development? Let’s Get Real
When I train junior developers, one of the most common questions I get is: “Do we really still need JavaScript Promises in modern development? Can’t we just use async/await and move on?”
It’s a fair question. Most of us write async/await daily, and Promises sometimes feel like an outdated concept we no longer touch. But here’s the catch: Promises are still the foundation of modern asynchronous JavaScript.
Let’s explore why they matter, when you should use them, and some real-life scenarios where Promises still shine.
What Problem Do Promises Solve?
Imagine you’re building a food delivery app. When a customer opens the app, you want to:
- Get their profile from the API
- Fetch their past orders
- Load the available restaurants nearby
If all of these were synchronous calls, the app would freeze until the server responded. That’s not acceptable.
In the old days, we’d chain callbacks like this:
1getUser(id, function (user) { 2 getOrders(user.id, function (orders) { 3 getRestaurants(user.location, function (restaurants) { 4 console.log('Done!') 5 }) 6 }) 7})
This “Christmas tree” code is what we call callback hell 🌲😩. Promises solved that problem by flattening async code into a manageable structure.
Promises in Action
Here’s the same food delivery logic with Promises:
1getUser(id) 2 .then(user => getOrders(user.id)) 3 .then(orders => getRestaurants(orders[0].location)) 4 .then(restaurants => console.log(restaurants)) 5 .catch(err => console.error("Something went wrong:", err));
Already much cleaner. But then async/await came along and made things even better.
Async/Await - The Modern Face
1async function loadDashboard(id) { 2 try { 3 const user = await getUser(id); 4 const orders = await getOrders(user.id); 5 const restaurants = await getRestaurants(user.location); 6 console.log("Nearby Restaurants:", restaurants); 7 } catch (err) { 8 console.error("Error loading dashboard:", err); 9 } 10}
This reads like synchronous code but works asynchronously. That’s why async/await became the standard. But don’t forget: every await is just sugar on top of a Promise.
Personal Note: Are you one of the folks that are used to resolve, reject or failing promise approach for all your RESTful API JavaScript related functions?
Haha, trust me - you’re not alone, my brother. A lot of developers (even experienced ones) get stuck in that same pattern of manually wrapping everything in new Promise(resolve, reject) because that’s how we first learned async JavaScript.
But here’s the truth: in modern JavaScript, every async function already returns a Promise. No resolve or reject required.
Take for instance, you want to register a new user with your sequelize ORM. Instead of the old “wrap it in a promise” way, here’s how much cleaner the modern async/await approach looks:
1const util = require("util"); 2const bcrypt = require("bcrypt"); 3const genSalt = util.promisify(bcrypt.genSalt); 4const hash = util.promisify(bcrypt.hash); 5 6registerUser: async (userData) => { 7 try { 8 const password = userData.password; 9 const salt = await genSalt(saltRounds); 10 const hashedPassword = await hash(password, salt); 11 userData.password = hashedPassword; 12 13 const count = await User.count({ where: { email: userData.email } }); 14 if (count > 0) { 15 return count; // user already exists 16 } 17 18 const user = await User.create(userData); 19 console.log(user.dataValues); 20 return user; 21 } catch (err) { 22 throw new Error(err); 23 } 24}
Notice how the async/await pattern keeps things simple and readable, without losing any control or functionality.
Real-Life Scenarios Where Promises Still Matter
So when should you care about Promises in modern development? Let’s look at real situations:
- Running Multiple API Calls in Parallel Imagine a travel booking site that fetches flight, hotel, and car rental data. Running them one by one is wasteful.
1const [flights, hotels, cars] = await Promise.all([ 2fetchFlights(), 3fetchHotels(), 4fetchCars() 5]); 6console.log("Search Results Ready:", { flights, hotels, cars });
Promise.all ensures they run at the same time, saving your users precious seconds.
- Taking the Fastest Response Let’s say you’re building a stock price app. You call multiple APIs but only need the fastest.
1const price = await Promise.race([ 2 fetchStockFromAPI1("AAPL"), 3 fetchStockFromAPI2("AAPL"), 4 fetchStockFromAPI3("AAPL") 5]); 6console.log("Fastest Stock Price:", price);
- Handling Background Tasks Think of a ride-hailing app like Uber. You might:
Send a push notification
Save the booking in the database
Trigger a background job to calculate driver arrival
These can run independently, and you don’t want to block the main flow:
1await Promise.allSettled([ 2 sendPushNotification(user.id), 3 saveBooking(ride), 4 calculateETA(driver.id) 5]);
- Retrying Failed Requests Promises also give you the flexibility to retry failed requests, which is crucial for unstable networks:
1function retry(fn, retries = 3) { 2 return fn().catch(err => { 3 if (retries > 1) return retry(fn, retries - 1); 4 throw err; 5 }); 6} 7 8await retry(() => fetch("/api/payment"), 3);
Mini Project Example: Weather Dashboard Task: Build a weather dashboard that loads:
Current weather from OpenWeather API
Air quality from a second API
Local time from a time zone API
Using Promise.all:
1async function loadWeather(city) { 2 try { 3 const [weather, airQuality, time] = await Promise.all([ 4 fetch(`/api/weather?city=${city}`).then(r => r.json()), 5 fetch(`/api/airquality?city=${city}`).then(r => r.json()), 6 fetch(`/api/time?city=${city}`).then(r => r.json()) 7 ]); 8 9 console.log("Weather:", weather); 10 console.log("Air Quality:", airQuality); 11 console.log("Local Time:", time); 12 } catch (err) { 13 console.error("Failed to load dashboard:", err); 14 } 15}
Best Practices in Modern Development
Use async/await for readability.
Use Promise.all for concurrency.
Handle errors with try/catch or .catch().
Use Promise.allSettled if you want all results regardless of failures.
Avoid deeply nested Promises, compose them instead.
Final Thoughts
So, are JavaScript Promises still necessary in modern development? 👉 Absolutely - 100%.
Even if you rarely write .then() anymore, Promises are the backbone of every async/await you use. They’re what makes modern web apps responsive, concurrent, and reliable.
The next time you await an API call, remember: there’s a Promise quietly doing the heavy lifting behind the scenes.
What about you? Do you still find yourself writing raw Promises, or do you rely entirely on async/await? I’d love to hear your experience and thoughts in the comments!