You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ConcatMap结合CombineLatest时请求次数超出预期问题排查

问题:带关联ID的顺序文件上传逻辑问题

我有一个NgRx Action/Effect,负责接收FileList并将文件依次上传到服务器。第一个文件上传完成后会生成一个ID,后续文件上传时需要传入这个ID,让后端建立关联关系。我用Component Store维护状态,里面包含初始值为null的ID和文件列表。

现在的问题是:第一个文件上传后更新Store中的ID时,用来保证顺序的concatMap还在处理所有文件,直到全部上传完成才会携带新ID生成新流,导致产生过多HTTP请求,而且后续文件都没用到第一个文件生成的ID。我不确定怎么在concatMap发起请求时获取Store中ID的最新值,猜测是combineLatest的位置不对。

原始尝试代码

readonly uploadFiles = this.effect((files: Observable<FileList>) => {
    return combineLatest([
        files.pipe(
            mergeMap(list => Array.from(list).map(i => ({file: i, length: list.length})) )
        ),
        // 存储第一个文件上传后生成的ID的Selector
        this.uploadId$,
    ]).pipe(
        concatMap(([tuple, uploadId], index) => 
            this._dataSvc.upload(tuple.file, index, tuple.length, uploadId.ID).pipe(
                tap({
                    next: (event) => {
                        switch (event.type) {
                          case HttpEventType.UploadProgress:
                            this.uploadProgress([
                                Math.round((event.loaded / event.total) * 100),
                                index
                            ]);
                            break;
                          case HttpEventType.Response:
                            if(uploadId.ID === null) {
                              // 调用Reducer更新当前上传批次的ID
                              this.setUploadId(event.body.ID);
                            }
                        }
                    },
                }),
                catchError(() => EMPTY) // 暂时忽略错误
            )
        )
    )
  });

根据@maxime1992建议修改后的代码

readonly uploadFiles = this.effect((files$: Observable<FileList>) => {
    return files$.pipe(
        concatMap((files) =>
            of({
                ID: null,
            }).pipe(
                expand((ids, index) =>{
                    const idx = index/5;
                    return files.length === idx || index % 5 !== 0 ? EMPTY :
                    this._dataSvc.upload(files[idx], idx, files.length, ids.ID).pipe(
                        tap({
                            next: (event) => {
                                switch (event.type) {
                                  case HttpEventType.UploadProgress:
                                    this.uploadProgress([
                                        Math.round((event.loaded / event.total) * 100),
                                        idx
                                    ]);
                                    break;
                                  case HttpEventType.Response:
                                    this.uploadCompleted(idx);
                                }
                            },
                        }),
                        map((event, index) => 
                            event.type === HttpEventType.Response ? 
                            ({ ID: event.body.ID }) : ({ ID: null })),
                        catchError(() => EMPTY) // 暂时忽略错误
                    )   
                })
            )
        )
    );

内容的提问来源于Stack Exchange,提问作者Luis Lavieri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:53:14