首页 > 解决方案 > 使用 RxJs 在 Angular 中按顺序执行一个后端调用并重试

问题描述

我有一些 Javascript 代码可以通过 API 调用从数据库中删除文档。文档之间存在内部依赖关系,因此删除可能会失败。因此代码将尝试在 5 轮中删除所有文档。

但是因为我在 Angular 上并且使用 RxJs 来完成所有其他任务,所以使用 RxJs 解决方案而不是 async/await 和 promises 会很好。我已经设法使用 concatMap 删除一轮,但是如何进行重试?

private async deleteDocuments(documents: IDocument[]) {
    let retries = 5;
    let remaining = [...documents];
    while (remaining.length > 0 && retries > 0) {
        remaining = await this.deleteDocumentsOneTry(remaining);
        retries--;
    }
}

private async deleteDocumentsOneTry(documents: IDocument[]) {
    const remaining: IDocument[] = [];
    for (const document of documents) {
        const deleted = await this.deleteDocument(document);
        if (deleted) {
            this.documentsDeleted++; // For progress bar
        } else {
            remaining.push(document);
        }
    }

    return remaining;
}

private deleteDocument(document: IDocument) {
    console.log(`Delete document ${document.name}`);
    return this.backend
        .deleteDocuments(document.id)
        .pipe(
            map(() => true),
            catchError(() => of(false))
        )
        .toPromise();
}

标签: javascriptangularrxjs

解决方案


考虑实现它,如下所示:

return from(documents).pipe(
  mergeScan((failed: number, document: IDocument) => {
    return that.backend.deleteDocuments(document.id).pipe(
      tap(x => this.documentsDeleted++),
      mapTo(failed),
      catchError((error, source) => failed < 5 ? source.pipe(mapTo(++failed)) : throwError(error))
    )
  }, 0)
)

简化实现:

let retries = 5;

from(documents).pipe(
  concatMap(doc => that.backend.deleteDocuments(doc.id).pipe(
    catchError((error, source) => --retries > 0 ? source : throwError(error)),
  ))
).subscribe((x) => this.documentsDeleted++)

推荐阅读