等待 Observable 完成以提交表单

Wait for Observable to complete in order to submit a form

我有一个 'new trip' 表单,用户可以在其中填写参与者的姓名,然后提交表单以创建行程。 提交时,我使用名称查询 Firebase 数据库,以获取参与者 (/users) 的 ID。然后我将 ID 添加到行程对象的 participantsID 字段,然后将新行程推送到 Firebase。

问题是 Firebase 查询是异步的并且 returns 是一个 Observable,因此我的函数将在 Observable 完成之前继续推送对象,因此新对象的 participantsID 字段为空。

有什么方法可以等待 observable 完成(以一种同步方式)以便我可以操作数据然后继续?到目前为止,我所有解决此问题的尝试都失败了。

这是我的简单代码。

getUserByAttribute(attribute, value) {
  return this.db.list('/users', {
    query: {
      orderByChild: attribute,
      equalTo: value,
      limitToFirst: 1
    }
  });
}

createTrip(trip) {
  for(let name in participantsName.split(',')) {
    getUserByAttribute('username', name)
      .subscribe( user => trip.participantsID.push(user[0].$key) );
  }

  this.db.list('/trips').push(trip);
}

你可以通过 forkJoin

将所有 Observables 处理成一个 Observable
createTrip(trip) {
  var observableArray: any = participantsName.split(',')
    .switchMap((name)=> getUserByAttribute('username', name))
  Observable.forkJoin(observableArray).subscribe(
    trips => trips.forEach((trip) => {
       this.db.list('/trips').push(trip);
    })
  );
} 

你遇到了一道难题。推送新行程前必须获取用户信息

由于内存泄漏问题,您不能每次都进行新订阅(或取消订阅时要小心)。如果您使用的是 Firebase,则可以使用 AngularFire subject 支持。

您可以通过在查询中使用主题(使用等于)来更新订阅,然后使用 .next(user) 推送用户进行检索。

那你还得等所有用户。为此,您可以只有一个订阅并同步获取所有 ID,或者有多个订阅以更快地获得多个结果(但这很困难)。

为了解决这个问题,我创建了:

  • 回调队列(只是数组,但使用 push() 和 unshift() 方法)

  • 值队列

  • 一个订阅一个主题。

如果您想要一个 ID,您必须:

  • 推送值
  • 推送将检索返回值的回调。

您应该使用函数来推送,因为如果堆栈中没有值(开始!),您将不得不调用 .next()。 在你的订阅中,在它的回调中,即当你收到远程用户对象时,你可以调用堆栈中的第一个回调。不要忘记弹出堆栈的值和回调,如果有下一个值,为下一个值调用 next()。

这样,您就可以在最后一个用户的最后一个回调中推送您的行程。而且都是回调,说明你的应用没有中断。

我还没有决定我们是否应该在云函数中这样做。因为用户必须保持连接,而这会使用他的数据/处理器。但是将所有代码放在同一个地方是件好事,而且对于免费版的 Firebase,云功能是有限的。 Firebase 开发人员会有什么建议?

为了找到更好的解决方案,我进行了很多搜索,如果有的话请分享。我认为这有点复杂,但它工作得很好。当用户想要添加新航班时,我遇到了同样的问题,我需要在(坐标)之前获取机场信息并推送多个对象(详细信息、地图等)

最后我使用了@Pankaj Parkar的部分回答解决了问题。 我 forkJoin 通过映射拆分名称返回的所有 Observable,我订阅了那个 Observable,结果包含一个数组数组,其中内部数组包含一个用户对象。

getUserByAttribute(attribute, value) {
  return this.db.list('/users', {
    query: {
      orderByChild: attribute,
      equalTo: value,
      limitToFirst: 1
    }
  }).first();
}

createTrip(trip) {
  Observable.forkJoin(
    trip.participantsName.split(',')
      .map(name => getUserByAttribute('name', name))
    ).subscribe(
      participants => {
        trip.participants = participants.map( p => p[0].$key);
        this.tripService.createTrip(trip);
      }
    );
  }   
}