TypeScript异步Promise填充Documentation对象异常问题求助
TypeScript异步请求填充数组问题修复方案
问题描述
我写了一段TypeScript代码,用来创建由文档路径和原始文本组成的Documentation对象列表。代码遍历CONFIG对象(键为章节名称,值为ConfigItem数组),收集文档路径及请求返回的Promise,随后调用Promise.all()尝试在所有数据就绪后一次性填充docs数组。
但运行时出现异常:浏览器控制台打印整个docs对象时能看到完整数据,紧接着遍历该对象却显示为空。我知晓这是get()请求返回Promise<Response>的异步特性导致的,但不知如何修复。相关代码如下:
const get = makeGetRequest('', 'application/text'); type Documentation = { path: string; rawText: string }; declare const CONFIG: Map<string, ConfigItem[]>; /* // from ConfigItem class export interface ConfigItem { subItem: boolean; source: string; } // from seperate class export declare const makeGetRequest: (apiUrl: string, dataType?: string) => (endpointUrl: string) => Promise<Response>; */ async function createDocs() { const docs: Documentation[] = []; const promises: { configItem: ConfigItem; promise: Promise<Response> }[] = []; CONFIG.forEach((value: ConfigItem[], key: string) => { value.forEach((configItem: ConfigItem) => { if (!configItem.subItem) { const promise = get(configItem.path); promises.push({ configItem, promise }); } }); }); await Promise.all(promises).then(values => { values.forEach(entry => { const doc: Documentation = { path: '', rawText: '' }; entry.promise .then(response => { if (!response.ok) { return Promise.reject(response); } else { return response.text().then(textValue => { doc.path = entry.configItem.path; doc.rawText = textValue; }); } }) .catch(e => Dialog.error('Failed to load page.', e)); docs.push(doc); }); }); console.log('here is docs object'); console.log(docs); (await docs).forEach(element => { console.log('element.name'); console.log(element.name); }); }
问题分析
- 重复处理Promise:
Promise.all(promises)已经会等待所有请求完成并返回结果,但你又在回调里重新调用entry.promise.then(),这不仅冗余,还导致填充doc的操作是在后续代码执行完之后才完成的。 - 提前推入空对象:你先把空的doc对象push进docs数组,之后才通过异步操作填充属性。遍历docs时,这些对象还没被填充,但浏览器控制台会实时更新对象的引用内容,所以你看到的是后续填充好的结果,遍历的时候却为空。
- 错误的await用法:最后
(await docs).forEach完全错误,docs是普通数组,不是Promise,不需要await。
修复后的代码
const get = makeGetRequest('', 'application/text'); type Documentation = { path: string; rawText: string }; declare const CONFIG: Map<string, ConfigItem[]>; /* // from ConfigItem class export interface ConfigItem { subItem: boolean; source: string; } // from seperate class export declare const makeGetRequest: (apiUrl: string, dataType?: string) => (endpointUrl: string) => Promise<Response>; */ async function createDocs() { // 构建一个直接返回Documentation对象的Promise数组 const docPromises: Promise<Documentation>[] = []; CONFIG.forEach((value: ConfigItem[]) => { value.forEach((configItem: ConfigItem) => { if (!configItem.subItem) { // 每个请求直接处理成Documentation对象的Promise const docPromise = get(configItem.path) .then(response => { if (!response.ok) { throw response; } return response.text(); }) .then(textValue => ({ path: configItem.path, rawText: textValue })) .catch(e => { Dialog.error('Failed to load page.', e); // 出错时可以返回一个空对象,或者根据需求处理 return { path: configItem.path, rawText: '' }; }); docPromises.push(docPromise); } }); }); // 等待所有Promise完成,直接得到填充好的docs数组 const docs = await Promise.all(docPromises); console.log('here is docs object'); console.log(docs); // 直接遍历数组即可 docs.forEach(element => { console.log('element.path'); console.log(element.path); console.log('element.rawText'); console.log(element.rawText); }); }
修复关键点
- 直接构建目标Promise:让每个Promise直接返回
Documentation对象,这样Promise.all之后就能直接得到完整的docs数组,不需要后续填充。 - 移除冗余异步操作:不再重复处理已完成的Promise,避免异步逻辑嵌套导致的时序问题。
- 简化异步代码:用
async/await替代部分then嵌套,让逻辑更清晰易读。 - 修正错误用法:去掉对普通数组的无效await,直接遍历已就绪的数组。
内容的提问来源于stack exchange,提问作者Barry
相关产品推荐
相关产品推荐

