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

Posted

技术标签:

【中文标题】通过使用查询而不是重复观察单个事件来加快获取我的社交网络应用程序的帖子【英文标题】:Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly 【发布时间】:2016-06-26 04:26:30 【问题描述】:

我有一组键,这些键会导致我的社交网络发布对象,例如 /posts/id/(post info)

当我加载帖子时,我使用 observeSingleEventOfType(.Value) 方法加载 /posts/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
        
    

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

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

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

【问题讨论】:

如果您向我们展示您的代码而不是描述它,这将更容易提供帮助。包括最小的 JSON(作为文本,而不是屏幕截图)和代码以重现您的问题中的问题,我们可以看到如何改进它。阅读有关MCVE 的更多信息。 已编辑以包含代码说明 【参考方案1】:

更新:我们现在也在 AskFirebase episode 中讨论了这个问题。

从 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 to demonstrate the behavior。数据模型非常简单,但它展示了差异。

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 毫秒。确切的数字取决于您的连接(延迟和带宽),但流水线版本应该总是更快。

【讨论】:

太好了,粉扑!此外,如果您需要加载所有项目,但仍希望在执行某些操作之前并行获取它们,则链接承诺(jQuery.whenAll()、q.all() 或 Promise.all())在这里非常方便。 酷。即使我一直在使用它,我什至没有想到这一点。 :-) @FrankvanPuffelen 从性能的角度来看,您是对的,但是如果其中一个调用由于任何类型的错误而没有返回怎么办?如果其中有人失败,您如何“取消”其余的未决请求。在顺序请求的情况下,我们可以在代码中知道哪个请求失败了。请分享你的想法。谢谢。 我们如何在android中做Promise.all?我们如何在android中加载所有数据 我在这里看到了一些有希望的***结果:google.com/…

以上是关于通过使用查询而不是重复观察单个事件来加快获取我的社交网络应用程序的帖子的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

从 Uri 获取单个查询参数 [重复]

带有单个查询的MYSQL多个COUNT()[重复]

XPATH - 获取返回的单个值而不是数组 php