等待 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);
}
);
}
}
我有一个 '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
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);
}
);
}
}