如何在遍历城市数组异步操作完成后发送响应?Node.js/MongoDB问题
Hey Carlos, I totally get why you're running into this issue—those asynchronous City.findOne calls inside your for loop don't finish executing before you send the response back, which leads to partial or empty citiesPopulate data. Let's fix this using modern async/await syntax and Promise.all to ensure all city coordinate queries complete first.
The Root Problem
Your original code uses nested callbacks for asynchronous database operations. The for loop fires off all City.findOne requests immediately, but Node.js doesn't wait for them to finish before moving on. If you tried sending the response right after the loop, it would run before any of the coordinate queries complete, resulting in incomplete data.
Fixed Code Using Async/Await & Promise.all
Here's a revised version of your function that ensures all coordinates are populated before sending the response:
async function findUsersCities(req, res) { try { const userId = req.body._id; // Fetch the user using await instead of callbacks const userFound = await User.findById(userId); if (!userFound) { return res.status(404).send({ message: "User not found" }); } // Create an array of promises, each fetching coordinates for a city const coordinatePromises = userFound.cities.map(cityName => { return City.findOne({ 'city': cityName }) .then(city => { // Handle cases where the city might not exist in the City collection return city ? city.coords : null; }); }); // Wait for all coordinate queries to resolve const allCoordinates = await Promise.all(coordinatePromises); // Filter out any null values (in case some cities weren't found) userFound.citiesPopulate = allCoordinates.filter(coord => coord !== null); // Now send the fully populated user data res.status(200).send({ userFound }); } catch (error) { console.error(error); res.status(500).send({ message: "Failed to fetch user or city coordinates", error: error.message }); } }
Key Improvements Explained
- Async/Await: Makes asynchronous code read like synchronous code, eliminating callback hell and improving readability.
- Promise.all: Collects all the city coordinate queries into an array of promises, then waits for every single one to complete before proceeding. This ensures we have all coordinates ready before updating
citiesPopulate. - Error Handling: The
try/catchblock catches any errors from fetching the user or city coordinates, and sends a proper 500 error response instead of just logging to the console. - Edge Case Handling: We check if the user exists and handle cases where a city might not be found in the
Citycollection (filtering out null values from the finalcitiesPopulatearray).
Why This Works
By wrapping each City.findOne call in a promise and using Promise.all, we guarantee that we won't proceed to send the response until all coordinate queries have resolved. This way, userFound.citiesPopulate will be fully populated with all available city coordinates when we send it back to the client.
内容的提问来源于stack exchange,提问作者Carlos FTG

