如何通过回调返回值?数据库查询回调处理问题技术求助
It looks like you're running into a common pitfall with asynchronous JavaScript code—your nbr_users function isn't returning the value you expect because the database query runs in the background, and the callback's return doesn't propagate back to the function's caller. Let's break down the problem and fix it step by step.
The Root Cause
Your current nbr_users function uses a callback to handle the database response, but callbacks are asynchronous. When you call nbr_users(key), it immediately returns undefined (since there's no top-level return statement in the function) before the database query finishes. The return users.length inside the callback only returns a value to the callback's invoker (the database library), not to your code that calls nbr_users.
Solution: Use Promises & Async/Await
Modern JavaScript provides cleaner ways to handle asynchronous code with Promises and async/await. Here's how to refactor your code:
Step 1: Rewrite nbr_users to Return a Promise
First, update nbr_users to return a Promise that resolves with the user count, or rejects with an error:
// Option 1: Explicit Promise wrapper (works with any callback-based db library) const nbr_users = (key) => { return new Promise((resolve, reject) => { db.User.find({ shop: key }, (err, users) => { if (err) { reject(err); // Pass error to the caller } else { resolve(users.length); // Pass the user count to the caller } }); }); }; // Option 2: Async/await shortcut (if your db library supports Promises, like Mongoose) const nbr_users = async (key) => { const users = await db.User.find({ shop: key }); return users.length; };
Step 2: Use Async/Await in Your Request Handler
Now, modify your exported request handler to be async, so you can await the user count for each account:
module.exports = async (req, res) => { try { // Fetch all accounts first const accounts = await db.Account.find({}); const accountMap = {}; // Process each account and get its user count for (const account of accounts) { // Wait for the user count to be retrieved const userCount = await nbr_users(account.shop); // Adjust the key to match your account structure // Add the account and user count to your map accountMap[account._id] = { ...account.toObject(), userCount }; } // Send the final map as the response res.json(accountMap); } catch (err) { // Handle any errors that occur during the process res.status(500).json(err); } };
Bonus: Parallelize Queries for Better Performance
If you have many accounts, processing them one by one with a for...of loop can be slow. Use Promise.all to run all user count queries in parallel:
module.exports = async (req, res) => { try { const accounts = await db.Account.find({}); // Create an array of promises to fetch user counts for all accounts const accountPromises = accounts.map(async (account) => { const userCount = await nbr_users(account.shop); return { [account._id]: { ...account.toObject(), userCount } }; }); // Wait for all promises to resolve const accountResults = await Promise.all(accountPromises); // Combine results into a single map const accountMap = Object.assign({}, ...accountResults); res.json(accountMap); } catch (err) { res.status(500).json(err); } };
Why This Works
- Promises let us wrap asynchronous operations and handle their results later.
- Async/await makes asynchronous code read like synchronous code, making it easier to follow and debug.
- Proper error handling with
try/catchensures any database errors are caught and sent back in the response.
内容的提问来源于stack exchange,提问作者Rbejot

