ExpressJS中Promise最佳实践:解决嵌套回调与异步等待问题
回调地狱优化方案
你已经在路由处理函数里用了async,直接用await替代嵌套的.then链,就能把代码改成线性结构,彻底摆脱回调地狱,同时统一错误处理逻辑:
app.get('/v1/example', async function(req, res) { let bearer; // 获取Bearer Token try { bearer = req.header('authorization').split(' ')[1]; } catch (e) { bearer = params.token; // 注意确认params是否在当前作用域内 } try { // 按顺序等待异步操作完成 await checkUser(bearer); await action1(bearer); const action2Result = await action2(bearer); res.json(action2Result.data); } catch (e) { // 统一处理所有异步错误 const status = e.response?.status || 500; const data = e.response?.data || '服务器内部错误'; res.status(status).send(data); } });
优化说明:
- 用
await替代.then,让异步代码看起来像同步代码,彻底消除嵌套 - 外层
try/catch统一捕获所有异步操作的错误,不用每个Promise单独写catch - 用可选链
?.简化错误对象的取值,避免嵌套try/catch - 移除原代码中重复的
res.json(r.data);笔误,确保响应只返回一次
轮询检查文件存在的优化方案
原代码的setInterval轮询存在执行重叠风险,且效率低下,推荐两种更优雅的实现方式:
方式1:Promise化递归轮询
把轮询封装成Promise,用递归setTimeout替代setInterval,避免前一次检查未完成就触发下一次:
const fs = require('fs').promises; // 使用fs的Promise API async function waitForFileExists(filePath, interval = 1000, maxAttempts = 10) { for (let i = 0; i < maxAttempts; i++) { try { await fs.access(filePath); return; // 文件存在,直接返回 } catch (err) { // 文件不存在,等待下一次轮询 await new Promise(resolve => setTimeout(resolve, interval)); } } throw new Error('文件超时未生成'); } // 使用示例 async function processImage() { const file = image_response.image_path; try { await waitForFileExists(file); const MIME = helpers.getMIME(image_response.extension); // 执行后续任务 } catch (err) { console.error(err); // 处理超时或错误 } }
方式2:事件驱动的文件监听
如果文件是由外部进程生成的,用fs.watch监听文件所在目录,比轮询更高效:
const fs = require('fs'); const path = require('path'); function waitForFileViaWatch(filePath, timeout = 30000) { return new Promise((resolve, reject) => { const dir = path.dirname(filePath); const fileName = path.basename(filePath); const watcher = fs.watch(dir, (eventType, filename) => { if (eventType === 'rename' && filename === fileName) { fs.access(filePath, err => { if (!err) { watcher.close(); resolve(); } }); } }); // 先检查文件是否已存在 fs.access(filePath, err => { if (!err) { watcher.close(); resolve(); } }); // 设置监听超时 setTimeout(() => { watcher.close(); reject(new Error('文件监听超时')); }, timeout); }); } // 使用示例 async function processImage() { const file = image_response.image_path; try { await waitForFileViaWatch(file); const MIME = helpers.getMIME(image_response.extension); // 执行后续任务 } catch (err) { console.error(err); } }
优化说明:
- 用异步的
fs.access替代同步的fs.existsSync,避免阻塞事件循环 - 递归
setTimeout比setInterval更可靠,不会出现任务重叠 fs.watch是事件驱动,只有文件变化时才触发操作,比轮询更高效- 两种方式都Promise化,配合
async/await使用,代码更整洁易维护
内容的提问来源于stack exchange,提问作者user3822343
相关产品推荐
相关产品推荐

