簡體   English   中英

如何使用RxJS運算符避免多個嵌套訂閱?

[英]How can I avoid multiple nested subscriptions using RxJS operators?

我正在使用Angular進行文件加密和上載類。 這些操作很多都是異步的,因此我編寫的方法返回RxJS Observables。

// 1.
private prepareUpload(file): Observable<T>;

// 2.
private encryptData(data, filekey): Observable<T>

// 3.
private uploadEncryptedData(formData, token, range): Observable<T>

// 4.
private completeUpload(updatedFilekey, token): Observable<T>

我想將此邏輯封裝在公共的upload(file)方法中,最終使用了嵌套訂閱,並且可以使用,但我知道這是錯誤的,並且出於多種原因,RxJS中使用了反模式。 這是代碼的簡化版本:

public upload(file) {
    const gen = this.indexGenerator(); // generator function

    this.prepareUpload(file).subscribe(values => {
    const [response, filekey, data] = values;

    this.encryptData(data, filekey).subscribe(encryptedDataContainer => {
      const formData = this.prepareEncDataUpload(encryptedDataContainer.data, file.name)
      const range = this.getRange(file.size, gen.next().value);

      this.uploadEncryptedData(formData, response.token, range).subscribe(() => {
        if (range.isFinalPart) {
            this.completeUpload(encryptedDataContainer.updatedFilekey, response.token).subscribe(console.log);
        }
      });

    });

  });

}

我無法使用多個RxJS運算符的組合來清除此代碼。 我的目標是避免嵌套訂閱,而是在工作流完成時從公共的upload()方法返回單個Observable。

謝謝!

您可以使用mergeMapfilter運算符並鏈接您的調用。 您將需要創建一些函數級變量以在鏈接期間使用。

import { mergeMap, filter, catchError } from 'rxjs/operators`
public upload(file) {
    const gen = this.indexGenerator(); // generator function
    let range, token;
    this.prepareUpload(file)
      .pipe(
        mergeMap((values) => {
          const [response, filekey, data] = values;
          token = response.token;
          return this.encryptData(data, filekey);
        }),
        mergeMap(encryptedDataContainer => {
          const formData = this.prepareEncDataUpload(encryptedDataContainer.data, file.name)
          range = this.getRange(file.size, gen.next().value);

          return this.uploadEncryptedData(formData, token, range);
        }),
        filter(() => !!range.isFinalPart),
        mergeMap(() => {
          return this.completeUpload(encryptedDataContainer.updatedFilekey, token);
        })
        catchError((error) => {
          console.log(error);
          // handle the error accordingly.
        })
      )
      .subscribe(() => {
        console.log('success');
      });

  }

您想要在訂閱之前使用管道。 管道允許您在流發出值之前對流中的值進行更改。 另外,使用mergeMap展平訂閱鏈。 這里是概述。 這不能提供完整的解決方案-付給我的錢還不夠;)-但這足以為您指明正確的方向:

this.prepareUpload(file).pipe(
  tap(values => console.log('hello1', values)),
  map(values => [response, filekey, data]),
  tap(values => console.log('hello2', values)),
  mergeMap(() =>
      // essential to catchError else an HTTP error response will disable this effect - if it uses HTTP - catchError essentially prevents the stream from erroring in which case it will never emit again
      this.encryptData(data, filekey).pipe(
        map(res => res), // do something with res here if you want
        catchError(() => {
          return of(null)
        })
      )
    ),
    filter(res => !!res)
    // more mergemap stuff here
  ).subscribe(values => {
    console.log(values)
  })

提示:使用tap運算符在流向下傳遞值時控制台.log值

PS:語法未選中,可能缺少逗號或方括號或2

PPS:管道中的函數都是RxJS運算符

您可以使用mergeMap rxjs運算符合並這些可觀察對象,並擺脫嵌套訂閱。

盡管有一個陷阱,但是請注意,由於mergeMap會同時維護多個活動的內部訂閱,因此有可能通過長期存在的內部訂閱造成內存泄漏。

作為參考和示例: https : //www.learnrxjs.io/operators/transformation/mergemap.html

我覺得你的鏈接觀測將做到這一點,你可以用flatMap(別名mergeMap)做到這一點,也許- https://stackoverflow.com/a/37777382/9176461RxJS無極組成(傳遞數據)

就像我在評論中所說的那樣,類似以下的東西應該起作用(偽代碼):

public upload(file) {
    const gen = this.indexGenerator(); // generator function

    return Rx.Observable.just(file).pipe(
         mergeMap(this.prepareUpload),
         mergeMap(this.encryptData),
         mergeMap(this.prepareEncDataUpload),
         mergeMap(this.prepareEncDataUpload),
         .... )
}

暫無
暫無

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

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