【问题标题】:Angular12/rxjs/typescript: Observable does not complete (inner/nested subscriptions)Angular12/rxjs/typescript:Observable 未完成(内部/嵌套订阅)
【发布时间】:2021-09-06 04:45:46
【问题描述】:

我有两个带有嵌套订阅的函数。

期望的行为:

  1. 更新用户数据
  2. 如果上一步成功 -> 更新用户图标
  3. 如果上一步成功或跳过/出错 -> 完成,只需使用返回的 url 路由
foo(): void {
    this.httpHandler.updateUser(userData).subscribe(
        (successUserData) => this.uploadAvatar().subscribe(
            (url) => this.route.navigate(url) //finish, some routing based on result url;
        )
    )
}

//returns an url or '' if skipped
uploadAvatar(): Observable<string> {
    let subject = new Subject<string>();

    if (!skipFileUpload) {
        this.httpHandler.uploadImage(this.file).subscribe(
            (successData) => {          
                this.httpHandler.verifySomething(successData.id).subscribe(
                   (verifiedData) => 
                       if (verifiedData) 
                          subject.next(verifiedData.url)
                )
            }
        )
    } else {
        subject.next('');
    }

return subject.asObservable();
}

问题:

如果文件没有上传,则 observable 不会解析。换句话说:subject.next(''); 不起作用,我的路由到 '' 没有发生。

但是,如果上传发生,那么一切都会按预期进行

整个过程是否可以扁平化或简化?

提前致谢。

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:

    您还没有编写错误处理程序:

        uploadAvatar(): Observable<string> {
        let subject = new Subject<string>();
    
        if (!skipFileUpload) {
            this.httpHandler.uploadImage(this.file).subscribe(
                (successData) => {          
                    this.httpHandler.verifySomething(successData.id).subscribe(
                       (verifiedData) => 
                           if (verifiedData) 
                              subject.next(verifiedData.url)
                    )
                },
                // ****  you had not handled the error case
                (error) =>  { subject.next(); subject.complete(); }
            );
        } else {
            subject.next('');
           subject.complete(); // else it will ramin a hot obervable
        }
    
    return subject.asObservable();
    }
    

    您可以使用 'concatMap' 'catchError' 'pipe' 跳过多个订阅

    【讨论】:

      【解决方案2】:

      您可以使用管道将其展平,而不是嵌套多个订阅。我根据您想要的行为修改了代码。

      首先是我们更新用户数据。 下一步,根据你贴的代码,我们会有一个条件是跳过文件上传,如果没有,我们调用上传图片api调用。 然后,如果上传图片调用成功,我们调用verifySomething api调用。 最后,当一切都成功后,我们路由到从verifySomething url返回的url。

      注意:我添加了条件来检查响应参数是否有 url 属性,因为当我们跳过 uploadImage api 调用时,我们可能会得到没有 url 的响应。

      updateProfile(): void {
       this.httpHandler
        .updateUser(userData)
        .pipe(
          mergeMap((e) => {
            if (!skipFileUpload) {
              return this.httpHandler.uploadImage(this.file).pipe(
                mergeMap((successData) => {
                  this.httpHandler.verifySomething(successData.id);
                })
              );
            } else {
              return of(e);
            }
          }),
          catchError((err) => {
            console.error(err);
            throw err;
          })
        )
        .subscribe((res) => {
          if (res && res.url) {
            this.route.navigate(res.url);
          }
        });
      }
      

      【讨论】:

        【解决方案3】:

        你可以使用 switchMap 来解决这个问题。

        foo(): void {
            this.httpHandler.updateUser(userData).pipe(
              switchMap(() => this.uploadAvatar())
            ).subscribe((url) => this.route.navigate(url))
        }
        
        //returns an url or '' if skipped
        uploadAvatar(): Observable<string> {
            let subject = new Subject<string>();
        
            if (!skipFileUpload) {
                const sub = this.httpHandler.uploadImage(this.file).pipe(
                  switchMap((successData) => this.httpHandler.verifySomething(successData.id))
                  tap((verifiedData) => subject.next(verifiedData ? verifiedData.url : ''))
                  catchError(e => { subject.next(''); return throwError(e) })
                ).subscribe();
            } else {
                subject.next('');
            }
        
        return subject.asObservable();
        }
        

        uploadAvatar,订阅应该被妥善管理。

        如果您只在foo 中使用updateAvatar 函数,那么您可以跳过额外的函数。

        foo(): void {
            this.httpHandler.updateUser(userData).pipe(
              map(() => skipFileUpload ? throwError('') : this.httpHandler.uploadImage(this.file))
              switchMap(obs => obs),
              map(successData => (successData && successData.id) ? this.httpHandler.verifySomething(successData.id) : throwError('')),
              switchMap(obs => obs),
              map(verifiedData => verifiedData ? of(verifiedData) : throwError('')),
              switchMap(obs => obs),
              catchError(err => { this.route.navigate(url); return throwError(err); })
            )
            ).subscribe((url) => this.route.navigate(url))
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-10
          • 2020-08-11
          • 1970-01-01
          • 1970-01-01
          • 2021-11-18
          • 2021-03-05
          • 2021-11-16
          • 2020-08-18
          相关资源
          最近更新 更多