ES6 async/await搭配String.replace时Promise未正常解析问题
问题原因
核心原因是JavaScript原生的String.prototype.replace方法不支持异步回调:
- 你传入的
async (match) => { ... }替换函数,执行后永远返回一个Promise对象 replace是完全同步的方法,拿到回调返回的Promise之后,不会等待Promise resolve,会直接将Promise转为字符串(也就是你看到的[object Promise])拼接到结果里- 你写的
await copyData.replace(...)没有任何实际等待效果:因为replace本身返回的是普通字符串,不是Promise,await一个非Promise值会直接返回该值,完全不会等待回调内部的异步数据库查询执行完成。
从你的日志顺序也能印证这个执行逻辑:先打印出替换后的包含[object Promise]的结果,之后才打印出2.subbedInstance: 5dbff89209dee20b18091ec3——说明数据库查询返回的时候,字符串替换早就结束了。
修复方案
不要在replace的回调里直接写异步逻辑,先把所有需要替换的匹配项找全,等所有异步查询完成拿到所有替换值之后,再做同步替换即可,参考实现:
const linearMongoObjectResolver = async (data) => { // 正则加g全局修饰符,支持替换所有匹配项,原正则无g只能替换第一个匹配内容 const dslRegex = /\${3}.*?#\)/g const matches = [...new Set(data.match(dslRegex) || [])] const replaceMap = new Map() // 先串行查询所有需要替换的引用值,匹配项多可以改成Promise.all并行查询提速 for (const matchStr of matches) { const parts = splitForeignKeyReference(matchStr) const queryObjJson = jsonify(parts[1]) let replacedVal = '""' if (parts[0] === 'StatisticDef') { // 注意补全.exec(),和你sinon mock的调用链保持一致,否则实际运行可能拿不到返回值 replacedVal = await StatisticDef.findOne(JSON.parse(queryObjJson)) .select('_id') .exec() } replaceMap.set(matchStr, replacedVal) } // 所有异步操作完成后,做纯同步替换 return data.replace(dslRegex, (match) => replaceMap.get(match)) }
额外注意点
- 原正则没有加
g全局修饰符,就算异步问题修复,也只能替换字符串里第一个符合规则的DSL,后续匹配项不会被处理 - 你的mock逻辑是
findOne -> select -> exec链才返回resolve的_id值,原实现里漏了.exec()调用,实际连接数据库运行时会拿不到预期结果
内容的提问来源于stack exchange,提问作者Michael Coxon
相关产品推荐
相关产品推荐

