等待Observable完成以提交表单

时间:2017-05-14 21:14:06

标签: javascript angular firebase observable angularfire2

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

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

是否有任何方法等待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);
}

3 个答案:

答案 0 :(得分:1)

您可以通过执行Observable

将所有Observable视为一个forkJoin
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);
    })
  );
} 

答案 1 :(得分:1)

最后,我使用@Pankaj Parkar的部分答案来解决问题。 我 public static void writeTGA(Bitmap src, String path) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(src.getRowBytes() * src.getHeight()); src.copyPixelsToBuffer(buffer); boolean alpha = src.hasAlpha(); byte[] data; byte[] pixels = buffer.array(); if (pixels.length != src.getWidth() * src.getHeight() * (alpha ? 4 : 3)) throw new IllegalStateException(); data = new byte[pixels.length]; for(int i=0;i < pixels.length; i += 4){// rgba -> bgra data[i] = pixels[i+2]; data[i+1] = pixels[i+1]; data[i+2] = pixels[i]; data[i+3] = pixels[i+3]; } byte[] header = new byte[18]; header[2] = 2; // uncompressed, true-color image header[12] = (byte) ((src.getWidth() >> 0) & 0xFF); header[13] = (byte) ((src.getWidth() >> 8) & 0xFF); header[14] = (byte) ((src.getHeight() >> 0) & 0xFF); header[15] = (byte) ((src.getHeight() >> 8) & 0xFF); header[16] = (byte) (alpha ? 32 : 24); // bits per pixel header[17] = (byte) ((alpha ? 8 : 0) | (1 << 4)); File file = new File(path); RandomAccessFile raf = new RandomAccessFile(file, "rw"); raf.write(header); raf.write(data); raf.setLength(raf.getFilePointer()); // trim raf.close(); } 通过映射分割名称返回所有Observable,我订阅了Observable,结果包含一个数组数组,其中内部数组包含一个用户对象。

forkJoin

答案 2 :(得分:0)

你遇到了一个棘手的问题。在推送新旅行之前,您必须获取用户信息。

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

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

然后你仍然需要等待所有用户。为此,您只能拥有一个订阅并同步获取所有ID或拥有多个订阅以更快地获得多个结果(但这很困难)。

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

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

  • 值列表

  • 一个订阅的主题。

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

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

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

这样,您可以在最后一个用户的最后一个回调中推送您的行程。这就是所有的回调,这意味着你的应用程序不会中断。

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

我做了很多搜索以找到更好的解决方案,所以如果你有一个,请分享。我认为这有点复杂,但它的工作非常好。当用户想要添加新航班时我遇到了同样的问题,我需要先获取机场信息(coords)并推送多个对象(详细信息,地图等)