【问题标题】:Angular map wait Observable to completeAngular 地图等待 Observable 完成
【发布时间】:2021-09-24 01:47:09
【问题描述】:

我试图在我的 Observable 函数中发送 httpclient,它会在没有 HttpClient Complete 的情况下运行。

这是一个用于重现的演示代码

    test() {
    this.test2()
      .pipe(
        mergeMap((result) => {
          console.log(result[0]);
          return of([]);
        })
      )
      .subscribe();
  }
  test1(): Observable<any> {
    return of(this.getStudents());
  }
  test2(): Observable<any> {
    return this.test1().pipe(
      mergeMap((result) => {
        if (result) {
          result.map((rr) => {
            if (rr.age == 1) {
              this.client
                .get('https://api.coindesk.com/v1/bpi/currentprice.json')
                .subscribe((res) => {
                  console.log(res);
                  rr.age = rr.age * 10000;
                });
            } else {
              rr.age = rr.age * 10;
            }
            return rr;
          });
        }
        return of(result);
      })
    );
  }
  getStudents(): Student[] {
    return [{ age: 1 }, { age: 2 }, { age: 3 }];
  }

这是Student

export class Student {
  age: number;
}

对于预期结果,console.log(res); 应该在console.log(result[0]); 之前返回。

我尝试了很多方法,例如.toPromiseasync await,但都没有成功。

您可以在下面创建一个测试版本:

https://stackblitz.com/edit/save-file-to-drive-7dawzd?file=src/app/app.component.ts

【问题讨论】:

    标签: angular typescript rxjs observable httpclient


    【解决方案1】:

    根据您的预期结果,您需要先完成 api 调用,然后再打印 Student[0]。

    代码中的问题:

    • 您正在订阅一个您不等待的 api 调用 完成,因此 console.log(result[0])console.log(res); 之前首先打印,因为 api 调用尚未完成。

    我使用了几个 RXJS 运算符来得到你想要的。

    • mergeMap 将内部 observable 展平
    • of 将学生数组转换为可观察对象
    • map 将当前数组转换为具有相应新年龄的新数组
    • forkJoin - 我们将多个请求封装到一个 observable 中,并且只有在收到所有请求的响应时才会返回。

    这当然只是一种方法,可能还有其他更好的方法。

    test() {
      this.test2()
        .pipe(
          mergeMap((result) => {
            console.log(result);
            if (result && result.length > 0) {
              console.log(result[0]);
            }
            return of([]);
          })
        )
        .subscribe();
    }
    test1(): Observable < Student[] > {
      // return of(null);
      return of(this.getStudents());
    }
    test2(): Observable < Student[] > {
      return this.test1().pipe(
        mergeMap((result) => {
          if (!result) {
            return of(result);
          }
          return forkJoin(
            result.map((rr) =>
              rr.age === 1 ?
              this.client
              .get('https://api.coindesk.com/v1/bpi/currentprice.json')
              .pipe(
                map((res) => {
                  console.log(res);
                  return (rr.age = rr.age * 10000);
                })
              ) :
              of ((rr.age = rr.age * 10))
            )
          ).pipe(
            map((paths) => {
              return result.map((e, index) => ({
                age: paths[index],
              }));
            })
          );
        })
      );
    }
    
    getStudents(): Student[] {
      return [{
        age: 1
      }, {
        age: 2
      }, {
        age: 3
      }];
    }
    

    我修改了您创建的 stackblitz 来模拟解决方案。

    【讨论】:

    【解决方案2】:

    RxJS 方式:

    虽然点击 api 端点并忽略它的结果很奇怪,但您可以确保使用几乎任何 RxJS 高阶运算符(如 mergeMap、switchMap、concatMap 等)等待该结果。

    由于您要对整个数组执行此操作,因此 forkJoin 是一个自然的选择。 forkJoin 将订阅您的所有 observables,并为每个完成后发出一个包含最后一个值的数组。

    test() {
      // mergeMap here is silly, it doesn't merge or map anything in a 
      // meaningful way. I've removed it.
      this.test2().subscribe(
        students => console.log(students[0])
      );
    }
    
    test1(): Observable<Student[]> {
      return of(this.getStudents());
    }
    
    test2(): Observable<Student[]> {
    
      return this.test1().pipe(
    
        map(students => students?.map(student => {
          if(student.age == 1){
            return this.client.get(
              'https://api.coindesk.com/v1/bpi/currentprice.json'
            ).pipe(
              tap(currentPrice => console.log(currentPrice)),
              mapTo({...student, age: student.age * 1000})
            )
          }else{
            return of({...student, age: student.age * 10})
          }
        })),
    
        mergeMap(studentCalls => forkJoin(studentCalls))
    
      );
    }
    
    getStudents(): Student[] {
      return [{ age: 1 }, { age: 2 }, { age: 3 }];
    }
    

    【讨论】:

    • 我尝试测试这段代码,它没有工作
    • @Edward 好的。为我工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 2018-08-06
    • 1970-01-01
    • 2017-09-20
    • 1970-01-01
    • 2021-06-20
    • 2021-11-16
    相关资源
    最近更新 更多