Angular/JHipster中服务调用返回值无法赋值问题求助
嗨,我来帮你理清这个问题~你遇到的确实是异步操作的典型问题:JavaScript/TypeScript里的HTTP请求都是异步的,当你调用nombreResponsable时,函数里的subscribe还没等请求完成就已经结束了,所以你拿到的responsable是undefined,而之后请求成功才会打印出admin这类值,这就是控制台输出顺序颠倒的原因。
1. 获取数据的正确方式
核心思路是:不要在nombreResponsable里直接subscribe,而是让它返回一个Observable,然后在调用的地方统一处理异步流。
首先修改nombreResponsable函数,让它返回Observable:
nombreResponsable(id): Observable<string | null> { return this.userService.findById(id).pipe( // 把响应转换成我们需要的login字段 map((res: HttpResponse<IUser>) => res.body.login), // 错误处理:捕获错误后提示,返回null避免整个流中断 catchError((res: HttpErrorResponse) => { this.onError(res.message); return of(null); }) ); }
然后在cargarActividades里,因为要处理多个异步请求(每个活动对应一个用户查询),我们可以用RxJS的forkJoin来并行处理所有请求,等所有请求都完成后再组装数据:
cargarActividades(id, meta, estrategia, id_meta) { this.activiadesDespliegueService.findByEstrategia(id).pipe( // 先拿到活动列表,再并行查询所有负责人名称 switchMap((res: HttpResponse<IActividadesDespliegue[]>) => { this.actividadesDespliegues = res.body; // 为每个活动创建一个查询负责人的Observable const responsableObservables = this.actividadesDespliegues.map(key => this.nombreResponsable(key.responsableId) ); // 把活动列表和负责人结果一起返回 return forkJoin(responsableObservables).pipe( map(responsables => ({ actividades: res.body, responsables })) ); }) ).subscribe( ({ actividades, responsables }) => { // 现在所有负责人数据都已经拿到了,可以安全组装 actividades.forEach((key, index) => { const responsable = responsables[index]; console.log(responsable); // 这里会正确输出名称 const datosActividad = { meta: meta, id: key.id, estrategia: estrategia, actividad: key.nombre, fecha: key.fechaCumpliniento, responsable: responsable, puntacion: key.puntacion }; this.cargarEvaluaciones(key.id); this.actividadesDespliegueEstrategico.push(datosActividad); this.listaDeMetas[id_meta].puntos += key.puntacion; }); }, (res: HttpErrorResponse) => this.onError(res.message) ); }
如果你习惯用async/await,也可以把Observable转换成Promise来处理,代码会更像同步写法:
cargarActividades(id, meta, estrategia, id_meta) { this.activiadesDespliegueService.findByEstrategia(id).subscribe( async (res: HttpResponse<IActividadesDespliegue[]>) => { this.actividadesDespliegues = res.body; // 并行获取所有负责人名称 const responsableObservables = this.actividadesDespliegues.map(key => this.nombreResponsable(key.responsableId) ); const responsables = await forkJoin(responsableObservables).toPromise(); // 组装数据 this.actividadesDespliegues.forEach((key, index) => { const responsable = responsables[index]; // ... 剩下的组装逻辑和上面一致 }); }, (res: HttpErrorResponse) => this.onError(res.message) ); }
2. 更优的实现方案
这里有几个可以优化的方向,按优先级排序:
(1)后端提供批量查询接口(最优)
现在你是在循环里发起多个用户查询请求,这会增加服务器压力,也会导致页面加载变慢。最好的方式是和后端开发人员沟通,新增一个批量查询用户的接口(比如findByIds(ids: number[])),这样你只需要把所有responsableId收集起来,发一次请求就能拿到所有用户的名称,性能会提升很多。
比如修改后,批量查询函数可以写成:
nombreResponsables(ids: number[]): Observable<Map<number, string>> { return this.userService.findByIds(ids).pipe( map((res: HttpResponse<IUser[]>) => { // 把用户列表转换成id->login的映射,方便后续快速查找 return new Map(res.body.map(user => [user.id, user.login])); }), catchError((res: HttpErrorResponse) => { this.onError(res.message); return of(new Map()); }) ); }
然后在cargarActividades里只需要一次请求:
cargarActividades(id, meta, estrategia, id_meta) { this.activiadesDespliegueService.findByEstrategia(id).pipe( switchMap((res: HttpResponse<IActividadesDespliegue[]>) => { this.actividadesDespliegues = res.body; const responsableIds = this.actividadesDespliegues.map(key => key.responsableId); return this.nombreResponsables(responsableIds).pipe( map(userMap => ({ actividades: res.body, userMap })) ); }) ).subscribe( ({ actividades, userMap }) => { actividades.forEach(key => { const responsable = userMap.get(key.responsableId); // ... 组装数据逻辑 }); }, (res: HttpErrorResponse) => this.onError(res.message) ); }
(2)用RxJS操作符统一管理异步流
Angular生态里RxJS是核心,尽量用它的操作符(比如switchMap、forkJoin、concatMap等)来处理异步逻辑,这样代码会更符合Angular的风格,也更容易维护和扩展。
(3)添加加载状态提示
因为异步请求需要时间,你可以在页面上添加加载动画,让用户知道正在处理数据,提升体验。比如在请求开始时设置isLoading = true,请求完成后设置isLoading = false。
内容的提问来源于stack exchange,提问作者Misael Landeros

