使用async函数创建新Promise是否属于不良实践?(Node.js MongoDB场景)
关于Promise嵌套async/await的Node.js MongoDB代码实践疑问
我的代码场景
我写了一段Node.js与MongoDB的CRUD应用代码,用来处理带JSON载荷的POST请求,完成数据创建操作。目前代码能正常运行,但我拿不准自己对Promise和async/await的使用是不是属于不良实践,想请教下大家。
我的代码片段
handlers._newbies = {}; handlers._newbies.post = (parsedReq, res) => { const newbie = JSON.parse(parsedReq.payload); databaseCalls.create(newbie) .then((result) => { res.writeHead(200,{'Content-Type' : 'application/json'}); const resultToString = JSON.stringify(result.ops[0]); res.write(resultToString); res.end(); }) .catch(err => console.log(err)); }; const databaseCalls = {}; databaseCalls.create = (newbie) => { return new Promise(async (resolve, reject) => { try { const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }); console.log("Connected correctly to server"); const db = client.db('Noob-List'); const result = await db.collection('newbies').insertOne(newbie); client.close(); resolve(result); } catch(err) { console.log(err); } }); };
我的疑问
当时写这段代码时,我希望databaseCalls.create能返回一个包含插入操作结果的Promise,但如果直接返回insertOne的Promise,就没办法调用client.close()了,所以才用了Promise嵌套async/await的写法。但我不确定这种写法合不合适,也没找到相关的明确说明,所以来求助。
解答
首先直接给结论:你当前的写法属于没必要的Promise嵌套,是可以优化的不良实践,下面给你拆解问题和优化方案:
问题出在哪?
async函数本身就会自动返回一个Promise,完全不需要你手动用new Promise()去包裹它。这种嵌套写法不仅冗余,还藏着一个坑:你在内部catch里只打印了错误,但没有调用reject(err),这会导致如果数据库操作出错,这个Promise会一直处于pending状态,外部的.catch()根本捕获不到这个错误!
优化后的写法
直接把databaseCalls.create定义成async函数就行,它会自动帮你处理Promise的返回,同时你照样能完成连接、插入、关闭的流程:
const databaseCalls = {}; databaseCalls.create = async (newbie) => { try { const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }); console.log("Connected correctly to server"); const db = client.db('Noob-List'); const result = await db.collection('newbies').insertOne(newbie); client.close(); return result; // async函数会自动把这个返回值包装成resolved状态的Promise } catch(err) { console.log(err); throw err; // 抛出错误,让外部的错误捕获逻辑能拿到这个错误 } };
额外的优化建议
- 完善错误传递:刚才提到的原代码错误捕获的问题,优化后用
throw err把错误抛出去,这样上层的.catch()或者try/catch就能捕获到,你还可以给客户端返回对应的错误响应,比如500状态码。 - 复用数据库连接:每次请求都创建新的MongoDB连接其实很低效,建议在应用启动的时候就创建一次连接,之后所有的数据库操作都复用这个连接,不用每次都打开关闭:
// 应用启动时初始化连接 let dbInstance; MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }) .then(client => { console.log("Connected correctly to server"); dbInstance = client.db('Noob-List'); }) .catch(err => console.error('数据库连接失败:', err)); // 之后的创建操作直接复用连接 databaseCalls.create = async (newbie) => { try { const result = await dbInstance.collection('newbies').insertOne(newbie); return result; } catch(err) { console.error('数据插入失败:', err); throw err; } };
- 处理器也改用async/await:你的请求处理器也可以改成async函数,让代码逻辑更连贯易读,不用再写
.then()链式调用:
handlers._newbies.post = async (parsedReq, res) => { try { const newbie = JSON.parse(parsedReq.payload); const result = await databaseCalls.create(newbie); res.writeHead(200,{'Content-Type' : 'application/json'}); res.write(JSON.stringify(result.ops[0])); res.end(); } catch(err) { console.error('请求处理失败:', err); // 给客户端返回错误响应 res.writeHead(500,{'Content-Type' : 'application/json'}); res.write(JSON.stringify({error: '服务器内部错误'})); res.end(); } };
内容的提问来源于stack exchange,提问作者Firecore
相关产品推荐
相关产品推荐

