Angular map 等待 Observable 完成

Angular map wait Observable to complete

我正在尝试在我的 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]); 之前 return。

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

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

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

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

您的代码存在的问题:

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

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

  • mergeMap 压平内部可观察对象
  • of 将 student 数组转换为 observable
  • map 将当前数组转换成一个新的数组,其对应的新年龄
  • forkJoin - 我们将多个请求包装到一个可观察对象中,并且仅当所有请求都收到响应时才会 return。

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

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 以模拟解决方案。

RxJS 方式:

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

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

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 }];
}