簡體   English   中英

如何使用 RxJS 以時間間隔調用多個依賴 api 調用

[英]How to call multiple dependent api calls with time intervals using RxJS

我正在嘗試在 angular 11 中為這樣的場景編寫代碼 -

我有文件列表,對於每個我點擊 api(比如 api1)的文件,我從響應中獲取一個 fileId,然后將它傳遞給另一個 api(比如 api2),我想繼續每 3 秒點擊一次 api2,除非我在響應中沒有得到 status="available"。 一旦我獲得了可用狀態,我不再需要為該 fileId 訪問 api2,我們可以開始處理循環中的下一個文件。

我擁有的每個文件的整個過程。

我知道我們可以使用 rxjs 運算符(如 mergeMap 或 switchMap)來實現這一點(因為序列現在對我來說並不重要)。 但我對 rxjs 很陌生,不知道如何把它放在一起。

這就是我現在正在做的——

this.filesToUpload.forEach((fileItem) => {
      if (!fileItem.uploaded) {
        if (fileItem.file.size < this.maxSize) {
          self.fileService.translateFile(fileItem.file).then( //hit api1
            (response) => {
              if (response && get(response, 'status') == 'processing') {
               //do some processing here 
               this.getDocumentStatus(response.fileId);
              } 
            },
            (error) => {
              //show error
            }
          );
        }
      }
   }); 
getDocumentStatus(fileId:string){
    this.docStatusSubscription = interval(3000)   //hitting api2 for every 3 seconds 
    .pipe(takeWhile(() => !this.statusProcessing))
    .subscribe(() => {
      this.statusProcessing = false;
      this.fileService.getDocumentStatus(fileId).then((response)=>{
        if(response.results.status=="available"){
          this.statusProcessing = true;
          //action complete for this fileId
        }
      },(error)=>{

      });
      
    })
    
  }

鑒於您所追求的內容的描述,這就是我可能會這樣做的方法。

  1. 創建您想要進行的所有調用的 observables 列表。
  2. 將列表連接在一起
  3. 訂閱

使這項工作起作用的是我們只訂閱一次(不是每個文件一次),並且我們讓操作員處理其他所有內容的訂閱和取消訂閱。

然后在我們訂閱之前什么都不會發生。 這樣concat可以為我們完成繁重的工作。 我們不需要使用this.statusProessing類的變量來跟蹤任何東西。 這一切都為我們處理好了。 這樣就不容易出錯了。

// Create callList. This is an array of observables that each hit the APIs and only
// complete when status == "available".
const callList = this.filesToUpload
  .filter(fileItem => !fileItem.uploaded && fileItem.file.size < this.maxSize)
  .map(fileItem => this.createCall(fileItem));

// concatenate the array of observables by running each one after the previous one
// completes.
concat(...callList).subscribe({
  complete: () => console.log("All files have completed"),
  error: err => console.log("Aborted call list due to error,", err)
});
createCall(fileItem: FileItemType): Observable<never>{
  // Use defer to turn a promise into an observable 
  return defer(() => this.fileService.translateFile(fileItem.file)).pipe(

    // If processing, then wait untill available, otherwise just complete
    switchMap(translateFileResponse => {
      if (translateFileResponse && get(translateFileResponse, 'status') == 'processing') {
        //do some processing here 
        return this.delayByDocumentStatus(translateFileResponse.fileId);
      } else {
        return EMPTY;
      }
    }),
    // Catch and then rethrow error. Right now this doesn't do anything, but If 
    // you handle this error here, you won't abort the entire call list below on 
    // an error. Depends on the behaviour you're after.
    catchError(error => {
      // show error
      return throwError(() => error);
    })

  );
}
delayByDocumentStatus(fileId:string): Observable<never>{
  // Hit getDocumentStatus every 3 seconds, unless it takes more
  // than 3 seconds for api to return response, then wait 6 or 9 (etc)
  // seconds.
  return interval(3000).pipe(
    exhaustMap(_ => this.fileService.getDocumentStatus(fileId)),
    takeWhile(res => res.results.status != "available"),
    ignoreElements(),
    tap({
      complete: () => console.log("action complete for this fileId: ", fileId)
    })
  );
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM