Angular 18中如何等待HttpClient订阅完成后再返回数据
问题描述
我正在使用Angular 18,尝试从API查询数据并存储到组件中。能在组件和数据获取服务中通过console.log成功打印存储API数据的对象,但单独打印对象的键或值时控制台显示undefined,使用Object.keys处理该对象也返回空数组。
我发现控制台展开的对象未准确反映当前值,但不确定修复方法。尝试延迟订阅返回数据以在其他代码执行前存入变量,但方法无效;让对象必须使用this关键字也无帮助。我尽量不想使用async/await(不想处理Promise),但无其他办法也可接受。
核心问题:如何等待HttpClient订阅完成后,再返回其查询的API数据?
原代码
api.service.ts
export class ApiService { constructor(private httpClient: HttpClient) { } private collection: Collection = { cards: {} }; queryApi(query: string): Observable<Data> { return this.httpClient.get<Data>(`https://api.scryfall.com/${query}`); } retrieveCard(name: string, set?: string): Cards { let card: Cards = {} this.queryApi(`cards/named?exact=${name}${typeof set != undefined ? `&set=${set}` : ""}`) .subscribe((json) => { card[json["id"]] = { name: json["name"], qty: 1, set: json["set"], imageURL: json["image_uris"]["png"] } }); return card; } }
add-cards.component.ts
export class AddCardsComponent { constructor(private apiService: ApiService) {} private tempCollection: Collection = { cards: {} }; name: string = ""; set: string = ""; addTempCard(): void { const card: Cards = this.apiService.retrieveCard(this.name.toLowerCase(), this.set?.toLowerCase()); console.log(card) // 返回键和值 console.log(Object.keys(card)[0]) // 返回undefined this.name = ""; this.set = ""; } }
解决方案
问题根源在于retrieveCard是同步返回空对象,但API请求是异步的——subscribe里的代码要等API响应回来才执行,此时方法已经提前返回了空的card,后续操作自然拿不到数据。下面提供两种修复方式:
方式一:返回Observable(推荐,符合Angular异步规范)
让服务层返回Observable,把API请求逻辑封装在数据流中,组件层订阅后再处理数据:
修改后的api.service.ts
export class ApiService { constructor(private httpClient: HttpClient) { } private collection: Collection = { cards: {} }; queryApi(query: string): Observable<Data> { return this.httpClient.get<Data>(`https://api.scryfall.com/${query}`); } retrieveCard(name: string, set?: string): Observable<Cards> { // 修正原参数判断逻辑的bug const queryParams = `cards/named?exact=${name}${set ? `&set=${set}` : ""}`; return this.queryApi(queryParams).pipe( map(json => { const card: Cards = {}; card[json["id"]] = { name: json["name"], qty: 1, set: json["set"], imageURL: json["image_uris"]["png"] }; return card; }) ); } }
修改后的add-cards.component.ts
export class AddCardsComponent { constructor(private apiService: ApiService) {} private tempCollection: Collection = { cards: {} }; name: string = ""; set: string = ""; addTempCard(): void { this.apiService.retrieveCard(this.name.toLowerCase(), this.set?.toLowerCase()) .subscribe(card => { console.log(card); // 此时能拿到完整数据 console.log(Object.keys(card)[0]); // 不再是undefined // 将card存入临时集合 Object.assign(this.tempCollection.cards, card); }); this.name = ""; this.set = ""; } }
方式二:使用async/await(转Promise)
如果想要同步写法的体验,可以把Observable转成Promise,用async/await处理:
修改后的api.service.ts
export class ApiService { constructor(private httpClient: HttpClient) { } private collection: Collection = { cards: {} }; queryApi(query: string): Observable<Data> { return this.httpClient.get<Data>(`https://api.scryfall.com/${query}`); } async retrieveCard(name: string, set?: string): Promise<Cards> { const queryParams = `cards/named?exact=${name}${set ? `&set=${set}` : ""}`; const json = await this.queryApi(queryParams).toPromise(); const card: Cards = {}; card[json["id"]] = { name: json["name"], qty: 1, set: json["set"], imageURL: json["image_uris"]["png"] }; return card; } }
修改后的add-cards.component.ts
export class AddCardsComponent { constructor(private apiService: ApiService) {} private tempCollection: Collection = { cards: {} }; name: string = ""; set: string = ""; async addTempCard(): Promise<void> { const card: Cards = await this.apiService.retrieveCard(this.name.toLowerCase(), this.set?.toLowerCase()); console.log(card); console.log(Object.keys(card)[0]); Object.assign(this.tempCollection.cards, card); this.name = ""; this.set = ""; } }
关键说明
- 控制台展开对象显示数据是因为浏览器会延迟解析对象引用——你看到的是对象最终状态,而非
console.log执行时的状态,这就是直接打印键会得到undefined的原因。 - Angular中优先推荐使用Observable处理异步操作,它比Promise更灵活,支持取消、重试、组合等操作,更适配Angular生态。
内容的提问来源于stack exchange,提问作者bascott
相关产品推荐
相关产品推荐

