通过使用查询而不是重复观察单个事件来加快获取社交网络应用程序的帖子的速度


97

我有一系列键,这些键可以为我的社交网络发布对象,如/ posts / id /(post info)

当我加载帖子时,我使用的observeSingleEventOfType(.Value)方法先加载/ posts / 0,然后加载/ posts / 1等。

我一次使用A lazyTableView加载30,速度非常慢。有什么方法可以使用一种查询方法,还是另一种可以使其更快的方法,即使我必须重组JSON树中的数据。

我来自Parse,它重新实现了我的应用程序,到目前为止,体验非常好。只是这一件事我有点坚持。在此先感谢您的帮助!

编辑:

func loadNext(i: Int) { 

    // check if exhists
    let ideaPostsRef = Firebase(url: "https://APPURL")

    ideaPostsRef.childByAppendingPath(i.description).observeSingleEventOfType(.Value, withBlock: {
        (snapshot) in

        if i % 29 == 0 && i != 0 && !self.hitNull { return }
            // false if nil
            // true if not nil
        if !(snapshot.value is NSNull) {
            let postJSON  = snapshot.value as! [String: AnyObject]
            print("GOT VALID \(postJSON)")
            let post = IdeaPost(message: postJSON["message"] as! String, byUser: postJSON["user"] as! String, withId: i.description)
            post.upvotes = postJSON["upvotes"] as! Int
            self.ideaPostDataSource.append(post)
            self.loadNext(i + 1)
        } else {
            // doesn't exhist
            print("GOT NULL RETURNING AT \(i)")
            self.doneLoading = true
            self.hitNull = true
            return
        }
    }
}

这个递归函数实际上是从firebase获取键号i的值。如果它是NSNULL,它将知道这是最后一个可能要加载的帖子,并且不再执行。如果未击中NSNULL,但i % 29 == 0它作为基本情况返回,因此一次仅加载30个帖子(索引为0)。当我设置doneLoading为时truetableView.reloadData()使用属性观察器调用。

这是我正在获取的数组的示例

"ideaPosts" : [ {
    "id" : 0,
    "message" : "Test",
    "upvotes" : 1,
    "user" : "Anonymous"
  }, {
    "id" : 1,
    "message" : "Test2",
    "upvotes" : 1,
    "user" : "Anonymous"
  } ]

1
如果您向我们展示代码而不是对其进行描述,将会更容易获得帮助。包括最低限度的JSON(作为文本,而不是屏幕截图)和代码,以在您的问题中重现该问题,我们可以看到如何对其进行改进。阅读有关MCVE的更多信息。
弗兰克·范·普菲伦

编辑后包含代码说明
Big_Mac '16

Answers:


123

更新:现在,我们还在AskFirebase情节中涵盖了这个问题。

从Firebase加载许多项目不必太慢,因为您可以通过管道传递请求。但是您的代码使这成为不可能,这确实会导致性能欠佳。

在您的代码中,您从服务器请求一个项目,等待该项目返回,然后加载下一个。在简化的序列图中,如下所示:

Your app                     Firebase 
                             Database

        -- request item 1 -->
                               S  L
                               e  o
                               r  a
                               v  d
                               e  i
        <-  return item  1 --  r  n
                                  g
        -- request item 2 -->
                               S  L
                               e  o
                               r  a
                               v  d
                               e  i
                               r  n
        <-  return item  2 --     g
        -- request item 3 -->
                 .
                 .
                 .
        -- request item 30-->
                               S  L
                               e  o
                               r  a
                               v  d
                               e  i
                               r  n
                                  g
        <-  return item 30 --

在这种情况下,您正在等待30倍的往返时间+ 30倍的时间从磁盘加载数据。如果(为简单起见)我们说往返需要1秒,而从磁盘加载项目也要花费至少1秒,即30 *(1 + 1)= 60秒。

在Firebase应用程序中,如果一次性发送所有请求(或至少合理数量的请求),您将获得更好的性能:

Your app                     Firebase 
                             Database

        -- request item 1 -->
        -- request item 2 -->  S  L
        -- request item 3 -->  e  o
                 .             r  a
                 .             v  d
                 .             e  i
        -- request item 30-->  r  n
                                  g
        <-  return item  1 --     
        <-  return item  2 --      
        <-  return item  3 --
                 .
                 .
                 .
        <-  return item 30 --

如果我们再次假设往返时间为1秒,加载时间为1秒,则您正在等待30 * 1 +1 = 31秒。

因此:所有请求都通过相同的连接。鉴于此,之间的唯一区别get(1)get(2)get(3)getAll([1,2,3])一些开销帧。

我设置了一个jsbin来演示行为。数据模型非常简单,但是却显示出了差异。

function loadVideosSequential(videoIds) {
  if (videoIds.length > 0) {
    db.child('videos').child(videoIds[0]).once('value', snapshot => {
      if (videoIds.length > 1) {
        loadVideosSequential(videoIds.splice(1), callback)
      }
    });
  }
}

function loadVideosParallel(videoIds) {
  Promise.all(
    videoIds.map(id => db.child('videos').child(id).once('value'))
  );
}

进行比较:在我的系统上顺序加载64个项目需要3.8秒,而以管道方式加载它们(如Firebase客户端本机一样)则需要600毫秒。确切的数字将取决于您的连接(延迟和带宽),但流水线版本应始终明显更快。


12
很好,Puf!此外,如果您需要加载所有项目,但是希望在执行某些操作之前并行抓取它们,那么在此处链接承诺(jQuery.whenAll(),q.all()或Promise.all())会非常方便。
加藤

5
凉。即使我一直在使用它,也没有想到。:-)
弗兰克·范·普菲伦

2
@FrankvanPuffelen从性能角度来看,您是正确的,但是如果这些调用之一由于任何类型的错误而没有返回怎么办?如果其中任何一个失败,如何“取消”其余的待处理请求。在有顺序请求的情况下,我们可以在代码中知道哪个请求失败。请分享您的想法。谢谢。
佩里

1
Promise.all()方法拒绝是因为第一个承诺被拒绝的原因。
pejalo

4
我们如何在Android中做Promise.all?我们如何在android中加载所有数据
Muhammad chhota
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.