如何使依赖于先前函数数据的函数等到设置该数据

凤凰

问题:我正在尝试使用第二个函数从 Firebase 检索信息,该函数依赖于从先前函数调用中设置的数据。但是,第二个函数在第一个函数设置数据之前执行。我知道这是因为函数在 Typescript / Angular 中的执行方式,但我不太熟悉使用异步函数。

问题:让第二个函数等到设置所需数据的最佳方法是什么?

附加信息:正在从 Firebase Firestore 存储/检索数据。我正在使用的集合包含任意数量的城市文件。这些城市文件中的每一个都包含一系列家庭。由于城市的数量可能会有所不同,我需要首先检索有家庭的城市列表,然后使用该列表检索其中的家庭。我尝试使用 Promises 来解决问题,尝试使函数异步(然后使用await),并制作回调函数,但没有运气。我在下面包含了我尝试过的解决方案。我还有更多需要包含的代码(或者如果我需要发布 Firestore 布局)请告诉我。

我也对检索/存储数据的其他解决方案持开放态度,只要它遵循与 Firestore 数据相同的格式。

代码

home.component.ts:

export class HomeComponent implements OnInit {
  activeCities: any = [];
  activeFamilies: Map<string, Family[]>;

  constructor(private fbService: FirebaseService) { }

  ngOnInit() {
    this.getActiveCities();
    this.getAllFamilies();
  }

  getActiveCities() {
    this.fbService.getActiveCities().subscribe(
      data => {
        this.activeCities = data;
      },
      error => {
        console.log("Error retrieving active cities");
      }
    );
  }

  getAllFamilies() {
    for (let city of this.activeCities) {
      this.fbService.getFamiliesForCity(city.id).subscribe(
        data => {
          let families: Family[] = [];
          families = data;
          this.activeFamilies.set(city .id, families);
        },
        error => {
          console.log("Error retrieving families for active cities");
        }
      );
    }
  }
}

firebase.service.ts:

export class FirebaseService {
  private activeCitiesPath = '/active_cities';
  constructor(private firestore: AngularFirestore) { }

  getActiveCities() {
    let colRef: AngularFirestoreCollection<any>;
    let temp: Observable<any[]>;
    let path = this.activeCitiesPath;
    colRef = this.firestore.collection(path);
    return colRef.snapshotChanges().pipe(
      map(actions => actions.map(a => {
        const data = a.payload.doc.data() as any;
        const id = a.payload.doc.id;
        return { id, ...data };
      }))
    );
  }

  getFamiliesForCity(cityCode: string) {
    let colRef: AngularFirestoreCollection<any>;
    let temp: Observable<any[]>;
    let path = this.activeCitiesPath + "/" + cityCode + "/families";
    colRef = this.firestore.collection(path);
    return colRef.snapshotChanges().pipe(
      map(actions => actions.map(a => {
        const data = a.payload.doc.data() as any;
        const id = a.payload.doc.id;
        return { id, ...data };
      }))
    );
  }

}

尝试的解决方案:我尝试了以下解决方案,但到目前为止都没有奏效:

承诺:

async ngOnInit() {
    let promises: Promise<void>[] = [];
    promises.push(this.getActiveCities());
    promises.push(this.getAllFamilies());

    Promise.all(promises).then(() => {
      console.log("All promises worked");
    }).catch(() => {
      console.log("Error in promise");
    });
}

private getActiveCities(): Promise<void>  {
    return new Promise<void>((resolve, reject) => {
        //same code but adding resolve(); and reject();
    });
}

private getAllFamilies(): Promise<void>  {
    return new Promise<void>((resolve, reject) => {
        //same code but adding resolve(); and reject();
    });
}

使用异步:

async ngOnInit() {
    await this.getActiveCities();
    await this.getAllFamilies();
}

通过回调,我尝试了类似于:https ://stackoverflow.com/a/21518470/5785332

我还尝试从类似问题的答案中实现解决方案:async/await in Angular `ngOnInit`

克里斯·汉密尔顿

最简单的解决方案是在您的第一个订阅中调用您的第二个函数:


  ngOnInit() {
    this.getActiveCities();
  }

  getActiveCities() {
    this.fbService.getActiveCities().subscribe(
      data => {
        this.activeCities = data;
        this.getAllFamilies();
      },
      error => {
        console.log("Error retrieving active cities");
      }
    );
  }

尽管更好的设计是将所有内容都保留为可观察对象并使用asynchtml 中的管道订阅。

export class HomeComponent implements OnInit {
  constructor(private fbService: FirebaseService) { }

  activeFamiliesMap = new Map<string, Observable<Family[]>>();

  activeCities$: Observable<any[]> = this.fbService.getActiveCities().pipe(
    tap((activeCities) => {
      for (const city of activeCities) {
        this.activeFamiliesMap.set(city.id, this.activeFamilies(city.id));
      }
    }),
    catchError((err) => {
      console.error('Error retrieving active cities', err);
      return of([]);
    })
  );

  activeFamilies(id: any): Observable<Family[]> {
    return this.fbService.getFamiliesForCity(id).pipe(
      catchError((err) => {
        console.error('Error retrieving families for city id:', id, err);
        return of([]);
      })
    );
  }
}

只是如何显示数据的示例:

<div>Active Cities</div>
<pre>{{ activeCities$ | async | json }}</pre>

<ng-container *ngFor="let city of activeCities$ | async">
  <div>City Id: {{ city.id }}</div>
  <div>Families:</div>
  <pre>{{ activeFamiliesMap.get(city.id) | async | json }}</pre>
</ng-container>

堆栈闪电战: https ://stackblitz.com/edit/angular-ivy-trkuqx ?file=src/app/app.component.ts


更好的设计可能是让您的服务返回地图的可观察对象,尽管它是可观察对象的丑陋野兽。至少它隐藏了组件的逻辑:

服务

  getFamilyMap(): Observable<Map<string, Family[]>> {
    return this.getActiveCities().pipe(
      map((activeCities) => {
        return activeCities.map((city) => {
          return this.getFamiliesForCity(city.id).pipe(
            map((families) => {
              return { id: city.id, families };
            })
          );
        });
      }),
      switchMap((data) => forkJoin(data)),
      map((data) => {
        const res = new Map<string, Family[]>();
        for (const entry of data) {
          res.set(entry.id, entry.families);
        }
        return res;
      }),
      catchError((err) => {
        console.error('Error retrieving family map', err);
        return of(new Map<string, Family[]>());
      })
    );
  }

零件

export class HomeComponent {
  constructor(private fbService: FirebaseService) {}

  activeCities$ = this.fbService.getActiveCities();
  familyMapEntries$ = this.fbService
    .getFamilyMap()
    .pipe(map((map) => Array.from(map)));
}

我使用Array.from()而不是map.entries()因为迭代器倾向于抛出changedAfterChecked错误。

html

<div>Active Cities</div>
<pre>{{ activeCities$ | async | json }}</pre>

<ng-container *ngFor="let entry of (familyMapEntries$ | async)">
  <div>City Id: {{ entry[0] }}</div>
  <div>Families:</div>
  <pre>{{ entry[1] | json }}</pre>
</ng-container>

堆栈闪电战: https ://stackblitz.com/edit/angular-ivy-ffzpya ?file=src/app/firebase.service.ts

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

您如何使用调度组来等待调用依赖于不同数据的多个函数?

如何返回依赖于两个函数的函数?

使用依赖于数据帧的多列的 if 语句编写 R 函数

返回依赖于函数内分配数据的惰性迭代器

对依赖于数据库的函数进行单元测试

如何将我的函数 arg 类型设置为相互依赖于配置结构?

如何设置依赖于临时最小化函数的约束?

如何(py)测试依赖于当前时间的函数?

如何使数组的大小依赖于构造函数参数?

如何使元素依赖于Angular中的数据属性?

当viewModel依赖于多个模型时如何访问数据

如何添加依赖于 *ngFor 内部数据的单个元素?

在数据透视表中:如何使用依赖于该维度的变量忽略表达式中的维度

动态依赖于数据jsonStore的changeItemTpl

是否有一个SQL函数可以让我输入依赖于另一列的某些数据

如何向量化/优化依赖于先前行的计算

Azure管道:如何使任务依赖于先前的任务?

是否可以使用依赖于该图表中信息的存在的数据填充 SQL 图表?

Javascript - 函数依赖于其他函数的结果

当我的JavaScript依赖于先前加载的脚本中的对象时,我该如何“保留”我的JavaScript?

FsCheck:如何生成依赖于其他测试数据的测试数据?

流星/铁路由器:如何等待依赖于其他数据的数据

Bitbake 依赖于 AAA 数据包,它将依赖于 AAA-dev

我该如何测试依赖于DomSanitizer的管道?

Python:如何从依赖于模块的另一个文件中正确导入函数

pytest我如何模拟依赖于其父调用参数的函数调用的副作用

朱莉娅:如何正确定义使用依赖于数字的类型的函数?

如何让Typescript知道依赖于传入参数的函数的返回类型

如何在init函数中实例化一个依赖于self的对象?