如何使用Promise或async/await替代setTimeout实现Express接口等待异步请求完成
解决方法
核心思路是把原本回调风格的异步逻辑封装为Promise,就可以用async/await等待执行完成,完全替代不可靠的setTimeout方案。
第一步:改造makeCall函数
首先我们需要把异步请求、XML解析的回调逻辑都包装到Promise中,同时修复原代码中全局myRequest可能引发的并发请求冲突问题:
const { promisify } = require('util'); const parseString = promisify(require('xml2js').parseString); // Node环境默认没有XMLHttpRequest,使用前需安装依赖:npm install xmlhttprequest const { XMLHttpRequest } = require('xmlhttprequest'); const makeCall = async (call) => { return new Promise((resolve, reject) => { // 每次请求新建独立实例,不要用全局共享的myRequest,避免并发请求数据混乱 const myRequest = new XMLHttpRequest(); myRequest.open('POST', 'http://111.222.3.444:55555'); myRequest.setRequestHeader('Content-Type', "application/x-www-form-urlencoded"); myRequest.send(call); myRequest.onload = async () => { if (myRequest.status === 200) { try { const result = await parseString(myRequest.responseText); dataList.unshift(result); resolve(result); // 把解析完成的结果直接返回 } catch (parseErr) { reject(parseErr); } } else { reject(new Error(`请求失败,状态码: ${myRequest.status}`)); } }; myRequest.onerror = () => reject(new Error('网络请求异常')); }); };
第二步:改造Express路由
将路由处理函数改为async函数,直接awaitmakeCall执行完成后再渲染页面:
app.get('/home', async (req, res) => { try { // 等待请求+XML解析完成后再继续执行,不需要硬等1秒 await makeCall(`<root> <slipped> <mail> <alike>-1845676614.3625278</alike> <paid>uncle</paid> <kill>something</kill> <name>Stephen</name> <men>such</men> <firm>rubbed</firm> <using>yesterday</using> </mail> </slipped> <pour>-1247721160</pour> <poet>language</poet> <sets>-1907281866</sets> <treated>proper</treated> <judge>781679047</judge> </root>`); res.render('home.ejs', { name: dataList[0].root.slipped.mail.name }); } catch (err) { // 统一捕获异常,避免请求无响应 console.error(err); res.status(500).send('服务器处理失败'); } })
补充说明
- 原方案的setTimeout属于固定延迟,不管请求什么时候完成都要等1秒,要是请求耗时超过1秒还是会拿到旧数据;优化后只要请求+解析完成就立即渲染,延迟更低也更可靠
- 新增了错误捕获逻辑,请求失败、XML解析失败时服务不会直接崩溃,可以根据业务需求调整错误返回内容
内容的提问来源于stack exchange,提问作者Hugobop
相关产品推荐
相关产品推荐

