通过使用查询而不是反复观察单个事件,加快为我的社交网络应用程序提取帖子

Big*_*Mac 88 ios firebase swift

我有一系列的键导致我的社交网络的帖子对象像/ posts/id /(发布信息)

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

我使用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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个递归函数基本上从firebase获取密钥号i的值.如果它是NSNULL,它知道这是最后一个可能加载的帖子,永远不会再做.如果NSNULL没有被击中,但是i%29 == 0则它返回作为基础案例,因此一次只加载30个帖子(0索引).当我observeSingleEventOfType(.Value)使用属性观察者调用doneLoading为true时.

这是我正在获取的数组的样本

"ideaPosts" : [ {
    "id" : 0,
    "message" : "Test",
    "upvotes" : 1,
    "user" : "Anonymous"
  }, {
    "id" : 1,
    "message" : "Test2",
    "upvotes" : 1,
    "user" : "Anonymous"
  } ]
Run Code Online (Sandbox Code Playgroud)

Fra*_*len 113

更新:我们现在也在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 --
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您等待30倍的往返时间+ 30倍于从磁盘加载数据所需的时间.如果(为了简单起见)我们说往返需要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 --
Run Code Online (Sandbox Code Playgroud)

如果我们再次假设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'))
  );
}
Run Code Online (Sandbox Code Playgroud)

为了进行比较:在我的系统上顺序加载64个项目需要3.8秒,同时加载流水线(因为Firebase客户端本身就是这样)需要600毫秒.确切的数字取决于您的连接(延迟和带宽),但流水线版本应该总是快得多.

  • 很好,Puf!此外,链接promises(jQuery.whenAll(),q.all()或Promise.all())在这里可以非常方便,如果您需要加载所有项目,但仍然希望在采取某些操作之前并行获取它们. (12认同)
  • 凉.甚至没想到,即使我一直在使用它.:-) (4认同)
  • @FrankvanPuffelen 从性能的角度来看,您是对的,但是如果其中一个调用由于任何类型的错误而没有返回怎么办?如果其中有人失败,您如何“取消”其余的待处理请求。在顺序请求的情况下,我们可以在代码中知道哪个请求失败了。请分享您的想法。谢谢。 (3认同)
  • 我们怎么能在android中做Promise.all?我们如何加载android中的所有数据 (3认同)
  • 我在这里看到了一些有希望的最佳结果:https://www.google.com/search?q=is+there+an+equivalent+of+Promise.all+in+swift (2认同)