循环调用含Promise的async函数后后续代码无法执行求助
问题分析与修复方案
首先,你的问题核心在于getProps函数的Promise结构存在两个关键问题:冗余的Promise嵌套和错误处理缺失,这导致当await this.view.getProps(id)执行时,一旦某个调用出现异常或者Promise未正确resolve/reject,整个循环会被卡住,后续代码(比如console.log("Hello"))永远无法执行。
问题拆解
先看你的getProps函数:
async getProps(dbId) { const properties = Viewer.properties //creates an empty map return new Promise((resolve, reject) => { let prom = new Promise((resolve, reject) => { this.gui.getProperties( dbId, args => { console.log('properties', args) resolve(args.properties) }, reject ) }) prom.then(props => { properties.set(dbId, props) resolve(properties.entries()) }) console.log('properties', properties) }) }
- 冗余的Promise嵌套:你已经在外层创建了一个Promise,里面又嵌套了另一个Promise,完全没必要,反而增加了复杂度。
- 错误处理漏洞:虽然你把
reject传给了getProperties的失败回调,但外层Promise的reject并没有被正确关联——如果getProperties调用失败,内层Promise会reject,但外层Promise的reject从未被调用,导致这个Promise永远处于pending状态,await会一直等待下去,循环卡住。 - 返回值问题:
properties.entries()返回的是Map迭代器,而不是你期望的“以ID为键、属性数组为值的映射”,虽然你说能打印,但迭代器并不是一个可直接使用的对象,可能后续处理会有问题。
修复后的getProps函数
我们可以简化Promise结构,正确处理resolve和reject,同时确保返回的是完整的Map(或者你需要的对象):
async getProps(dbId) { // 确保Viewer.properties是一个Map,如果每次调用需要独立映射,就改为new Map() const properties = Viewer.properties || new Map(); return new Promise((resolve, reject) => { this.gui.getProperties( dbId, (args) => { console.log('properties', args); properties.set(dbId, args.properties); // 返回完整的Map,而不是迭代器,方便调用方直接使用 resolve(properties); }, (error) => { console.error('Failed to get properties for dbId:', dbId, error); // 必须调用reject,否则Promise会一直处于pending状态 reject(error); } ); }); }
修复点说明:
- 去掉了冗余的内层Promise,直接用外层Promise包装
getProperties的回调逻辑。 - 明确处理失败回调,调用外层的
reject,确保Promise不会一直挂起。 - 返回完整的
propertiesMap,替代迭代器,让调用方可以直接使用get(id)或者转为普通对象。
修复调用逻辑
在getConcreteIds中,await会等待每个getProps完成,一旦某个调用失败,整个async函数会抛出错误,导致后续代码不执行。建议添加try/catch处理异常,确保循环能继续执行:
async getConcreteIds() { let wallfloorids = this.getWallIds().concat(this.getFloorIds()); // 用于收集所有成功获取的属性映射 const concretePropsMap = new Map(); for(let id of wallfloorids) { try { let propsMap = await this.view.getProps(id); console.log(propsMap); // 将当前ID的属性存入全局收集Map concretePropsMap.set(id, propsMap.get(id)); } catch (error) { console.error('Failed to process dbId:', id, error); // 跳过错误ID,继续处理下一个 continue; } } console.log("Hello"); // 返回最终的属性映射 return concretePropsMap; }
调用逻辑说明:
- 用
try/catch包裹每个await调用,即使某个ID处理失败,循环也会继续执行,不会卡住整个流程。 - 单独收集成功的结果,方便后续业务逻辑使用。
额外优化建议
如果Viewer.properties是全局共享的Map,多次调用getProps会持续往里面添加数据,这可能符合你的预期;但如果每次调用需要独立的映射,应该在getProps里创建新的Map():
const properties = new Map(); // 替代复用Viewer.properties
这样调整后,你的循环会正常执行完所有52个ID,后续的console.log("Hello")也会正常打印,同时能得到你期望的ID-属性映射。
内容的提问来源于stack exchange,提问作者Daniel Ignjat
相关产品推荐
相关产品推荐

