Accélérez la récupération des publications pour mon application de réseau social en utilisant une requête au lieu d'observer un seul événement à plusieurs reprises

98

J'ai un tableau de clés qui mènent à publier des objets pour mon réseau social comme so / posts / id / (post info)

Lorsque je charge les messages, je charge / posts / 0 puis / posts / 1 etc. en utilisant la observeSingleEventOfType(.Value)méthode.

J'utilise un lazyTableViewpour charger 30 à la fois et c'est assez lent. Est-il possible d'utiliser l'une des méthodes de requête ou un autre moyen de l'accélérer même si je dois restructurer les données dans mon arbre JSON.

Je viens de Parse réimplémentant mon application et jusqu'à présent, l'expérience a été plutôt bonne. Juste cette chose sur laquelle je suis un peu coincé. Merci d'avance pour l'aide!

ÉDITER:

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
        }
    }
}

Cette fonction récursive exécute essentiellement l'obtention de la valeur du numéro de clé i à partir de Firebase. Si c'est NSNULL, il sait que c'est la dernière publication possible à charger et ne le fait plus jamais. Si NSNULL n'est pas atteint, mais i % 29 == 0il retourne comme cas de base, donc seuls 30 messages sont chargés à la fois (0 indexé). Lorsque je définis doneLoadingsur true, tableView.reloadData()est appelé à l'aide d'un observateur de propriété.

Voici un exemple de ce à quoi ressemble le tableau que je récupère

"ideaPosts" : [ {
    "id" : 0,
    "message" : "Test",
    "upvotes" : 1,
    "user" : "Anonymous"
  }, {
    "id" : 1,
    "message" : "Test2",
    "upvotes" : 1,
    "user" : "Anonymous"
  } ]
Big_Mac
la source
1
Ce sera beaucoup plus facile d'aider si vous nous montrez votre code au lieu de le décrire. Incluez le JSON minimum (sous forme de texte, pas de capture d'écran) et de code pour reproduire le problème dans votre question et nous pouvons voir comment il peut être amélioré. En savoir plus sur un MCVE .
Frank van Puffelen
Modifié pour inclure l'explication du code
Big_Mac

Réponses:

124

Mise à jour: nous couvrons désormais également cette question dans un épisode AskFirebase .

Le chargement de nombreux éléments depuis Firebase ne doit pas nécessairement être lent, car vous pouvez canaliser les requêtes. Mais votre code rend cela impossible, ce qui entraînera en effet des performances sous-optimales.

Dans votre code, vous demandez un élément au serveur, attendez le retour de cet élément, puis chargez le suivant. Dans un diagramme de séquence simplifié qui ressemble à:

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 --

Dans ce scénario, vous attendez 30 fois votre temps aller-retour + 30 fois le temps nécessaire pour charger les données à partir du disque. Si (par souci de simplicité) nous disons que les allers-retours prennent 1 seconde et que le chargement d'un élément à partir du disque prend également une seconde au moins à 30 * (1 + 1) = 60 secondes.

Dans les applications Firebase, vous obtiendrez de bien meilleures performances si vous envoyez toutes les demandes (ou au moins un nombre raisonnable d'entre elles) en une seule fois:

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 --

Si nous supposons à nouveau un aller-retour d'1 seconde et 1 seconde de chargement, vous attendez 30 * 1 + 1 = 31 secondes.

Donc: toutes les demandes passent par la même connexion. Étant donné que, la seule différence entre get(1), get(2), get(3)et getAll([1,2,3])est une surcharge pour les cadres.

J'ai mis en place un jsbin pour démontrer le comportement . Le modèle de données est très simple, mais il montre la différence.

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'))
  );
}

À titre de comparaison: le chargement séquentiel de 64 éléments prend 3,8 secondes sur mon système, tandis que leur chargement en pipeline (comme le client Firebase le fait en natif) prend 600 ms. Les chiffres exacts dépendront de votre connexion (latence et bande passante), mais la version en pipeline devrait toujours être beaucoup plus rapide.

Frank van Puffelen
la source
12
Bien, Puf! En outre, le chaînage des promesses (jQuery.whenAll (), q.all () ou Promise.all ()) peut être très pratique ici si vous avez besoin de tous les éléments chargés, mais que vous souhaitez toujours les saisir en parallèle, avant de prendre des mesures.
Kato
5
Cool. Je n'ai même pas pensé à ça, même si je l'utilise. :-)
Frank van Puffelen
2
@FrankvanPuffelen Vous avez raison du point de vue des performances, mais que se passe-t-il si l'un de ces appels ne revient pas en raison d'une erreur quelconque? Comment pouvez-vous `` annuler '' le reste des demandes en attente si l'une d'entre elles échoue. En cas de requêtes séquentielles, nous pouvons savoir dans le code quelle requête a échoué. Veuillez partager vos pensées. Merci.
Perry
1
" La méthode Promise.all () [...] rejette avec la raison de la première promesse qui rejette."
pejalo
4
Comment pouvons-nous faire Promise.all dans Android? Comment nous pouvons charger toutes les données dans Android
Muhammad chhota