Angular 可观察管道 return 数组

Angular observable pipe return array

我是 Angular 9 的新手,基本上我想知道如何 return 来自异步源的对象数组?我目前有一个 firestore 查询,其中 returned 的文档有一个我希望呈现其数据的引用。

ngOnInit

this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
            .pipe(map(querySnap => {
                const ret = [];
                querySnap.forEach(doc => {
                    const val = doc.get('otherUser').get().then(userData => {return userData});
                    ret.push(val);
                });
                return ret;
            })).subscribe(val => {
                this.links = val;
        });

HTML

<ion-item *ngFor="let link of links | async">
            <ion-avatar slot="start">
                <img [src]="getImage(link.get('profilepic'))">
            </ion-avatar>
            <ion-label>
                <h2>{{link.get('name')}}</h2>
                <p>{{link.get('bio')}}</p>
            </ion-label>
            <ion-checkbox id="{{link.id}}"></ion-checkbox>
        </ion-item>

当前 return 是错误:InvalidPipeArgument:'[object Promise]' for pipe 'AsyncPipe' 当我删除 HTML 中的异步管道时,它 return 是一个 zoneawarepromise。

编辑

this.links = this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
            .pipe(map(querySnap => {
                const ret = [];
                querySnap.forEach(async doc => {
                    const val = await doc.get('otherUser').get().then(userData => {return userData});
                    ret.push(val);
                });
                return ret;
            }));

现在可以使用了,但是我不知道该解决方案对于大量文档的效率和可扩展性如何,因为 ret数组 注意:querySnap 是一个对象,它有一个内置的 forEach 方法来遍历数组 docs 属性.

你能试试这个吗:

            .pipe(map(querySnap => {
                const ret = [];
                querySnap.forEach(doc => {
                    const val = doc.get('otherUser').get().then(userData => {return userData});
                    ret.push(val);
                });
                return ret;
            })).map(val => {
                this.links = of(val);
        });

基本上,您刚开始将可观察流添加到链接变量。

通常不建议混合使用 promises 和 observables。它们确实可以很好地协同工作,但它会使您的代码混乱和混乱。将 promise 转换为 observable 也非常简单。大多数 create/deal 具有高阶可观察量的运算符都会为您进行转换。否则,from(promise) return 也是一个可观察对象。

使用 forkJoin

在这种情况下,forkJoin毫无怨言地接受了Promise的数组。

forkJoin 将同时 运行 所有承诺和 return 一系列响应 once/if 它们都是 complete/resolve.

this.links = this.currentUserRef.collection(
  'links', 
  ref => ref.where('pendingRequest', '==', false)
).get().pipe(
  mergeMap(querySnap => forkJoin(
    querySnap.docs.map(
      doc => doc.get('otherUser').get()
    ))
  )
);

使用连接

concat 也可以接受 promises 作为参数。我们使用扩展运算符 (...) 将数组转换为参数列表。

这实际上更接近您通过承诺实现的目标,因为您 await 每个承诺都在 运行 下一个承诺之前。这将 运行 你的承诺一次一个(而不是同时, forkJoin 的方式

this.links = this.currentUserRef.collection(
  'links', 
  ref => ref.where('pendingRequest', '==', false)
).get().pipe(
  mergeMap(querySnap => concat(
    ...querySnap.docs.map(
      doc => doc.get('otherUser').get()
    ))
  ),
  toArray()
);

旁白:清理您的承诺代码

这个:

this.links = this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
            .pipe(map(querySnap => {
                const ret = [];
                querySnap.forEach(async doc => {
                    const val = await doc.get('otherUser').get().then(userData => {return userData});
                    ret.push(val);
                });
                return ret;
            }));

相当于

this.links = this.currentUserRef.collection(
  'links', 
  ref => ref.where('pendingRequest', '==', false)
).get().pipe(
  map(querySnap => {
    const ret = [];
    querySnap.forEach(async doc => 
      ret.push(await doc.get('otherUser').get())
    );
    return ret;
  })
)

基本上,如果您使用 await,则不需要 .then(...