多帖子评论提交:并发调用单API时如何显示带百分比的加载状态?
解决方案:用RxJS实现带进度追踪的批量评论提交
针对你的需求,我们可以通过RxJS的from+mergeMap+scan组合替代forkJoin,实现实时进度追踪、单个请求状态记录的批量提交。
实现步骤
1. 定义进度状态结构
先定义接口维护批量提交的进度和结果:
interface BatchProgress { total: number; // 总请求数 completed: number; // 已完成请求数 percentage: number; // 完成百分比 successes: Array<{ postId: number; response: any }>; // 成功请求列表 failures: Array<{ postId: number; error: any }>; // 失败请求列表 }
2. 封装批量提交函数
基于单个评论保存API,封装带进度追踪的批量提交逻辑:
import { from, Observable, of } from 'rxjs'; import { catchError, map, mergeMap, scan } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; // 帖子类型定义 interface Post { userId: number; postId: number; title: string; body: string; pictures: string[]; date: string; lastModifiedDate: string; } export class CommentService { constructor(private http: HttpClient) {} // 单个评论保存API调用 private saveSingleComment(postId: number, comment: string): Observable<any> { return this.http.post(`http://localhost:4200/post/${postId}`, { comment }); } // 批量提交评论,接收包含帖子和对应评论的数组 submitBatchComments(postsWithComments: Array<{ post: Post; comment: string }>): Observable<BatchProgress> { const totalRequests = postsWithComments.length; // 初始进度状态 const initialProgress: BatchProgress = { total: totalRequests, completed: 0, percentage: 0, successes: [], failures: [] }; return from(postsWithComments).pipe( // 并发发起请求,第二个参数控制最大并发数(避免请求过载) mergeMap(item => this.saveSingleComment(item.post.postId, item.comment).pipe( // 成功时包装结果,带上postId map(response => ({ type: 'success' as const, postId: item.post.postId, response })), // 捕获单个请求错误,返回失败结果(不中断整个批量流) catchError(error => of({ type: 'failure' as const, postId: item.post.postId, error })) ), 3 // 示例:同时最多发起3个并发请求 ), // 累计进度和结果 scan((progress, result) => { const newCompleted = progress.completed + 1; const updatedSuccesses = [...progress.successes]; const updatedFailures = [...progress.failures]; if (result.type === 'success') { updatedSuccesses.push({ postId: result.postId, response: result.response }); } else { updatedFailures.push({ postId: result.postId, error: result.error }); } return { ...progress, completed: newCompleted, percentage: Math.round((newCompleted / totalRequests) * 100), successes: updatedSuccesses, failures: updatedFailures }; }, initialProgress) ); } }
3. 组件中使用并更新UI
在组件里订阅Observable,实时更新加载状态、进度条和结果展示:
import { Component } from '@angular/core'; import { CommentService } from './comment.service'; @Component({ selector: 'app-comment-submit', template: ` <div *ngIf="progress"> <div>提交进度:{{ progress.percentage }}%</div> <progress [value]="progress.percentage" max="100"></progress> <h3>成功提交</h3> <ul> <li *ngFor="let item of progress.successes">帖子ID {{ item.postId }}:提交成功</li> </ul> <h3>提交失败</h3> <ul> <li *ngFor="let item of progress.failures">帖子ID {{ item.postId }}:{{ item.error.message }}</li> </ul> </div> <button (click)="startBatchSubmit()">批量提交</button> ` }) export class CommentSubmitComponent { progress: BatchProgress | null = null; // 假设这里是用户输入了评论的帖子列表 postsWithComments = [ { post: { userId: 1, postId: 1, title: "Hello World", body: "post body will contain large text", pictures: ["1.jpg", "2.jpg"], date: "01/01/1970", lastModifiedDate: "01/02/1970" }, comment: "这是帖子1的评论" }, // 更多带评论的帖子... ]; constructor(private commentService: CommentService) {} startBatchSubmit() { this.commentService.submitBatchComments(this.postsWithComments).subscribe(progress => { this.progress = progress; // 所有请求完成时的额外处理 if (progress.completed === progress.total) { console.log("批量提交全部完成"); } }); } }
核心逻辑说明
from(postsWithComments):将帖子数组转换为Observable序列,逐个发出带评论的帖子对象mergeMap:对每个帖子发起API请求,通过catchError确保单个请求失败不中断整个流程;第二个参数控制并发数,避免服务器/浏览器过载scan:实时累计请求结果,计算完成百分比,维护成功/失败列表,每次发出最新进度供UI更新- 对比
forkJoin:无需等待所有请求完成即可反馈进度,单个失败不影响其他请求执行
可选调整
- 若需串行提交(逐个执行请求),将
mergeMap替换为concatMap即可,无需设置并发数 - 可通过
bufferCount运算符调整进度更新粒度,比如每完成N个请求更新一次UI
内容的提问来源于stack exchange,提问作者coloraddict
相关产品推荐
相关产品推荐

