Accelera il recupero dei post per la mia app di social network utilizzando la query invece di osservare ripetutamente un singolo evento


98

Ho una serie di chiavi che portano a postare oggetti per il mio social network come così / posts / id / (informazioni sul post)

Quando carico i post, carico / posts / 0 e poi / posts / 1 ecc. Usando il observeSingleEventOfType(.Value)metodo.

Uso un lazyTableViewper caricare 30 alla volta ed è piuttosto lento. Esiste un modo per utilizzare uno dei metodi di query o un altro modo per renderlo più veloce anche se devo ristrutturare i dati nel mio albero JSON.

Vengo da Parse che reimplementa la mia app e finora l'esperienza è stata abbastanza buona. Solo questa cosa su cui sono un po 'bloccato. Grazie in anticipo per l'aiuto!

MODIFICARE:

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

Questa funzione ricorsiva viene eseguita essenzialmente ottenendo il valore per la chiave numero i da Firebase. Se è NSNULL sa che è l'ultimo post possibile da caricare e non lo fa mai più. Se NSNULL non viene colpito ma i % 29 == 0viene restituito come caso base, vengono caricati solo 30 post alla volta (0 indicizzati). Quando impostato doneLoadingsu true, tableView.reloadData()viene chiamato utilizzando un osservatore di proprietà.

Ecco un esempio di come appare l'array che sto recuperando

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

1
Sarà molto più facile aiutarci se ci mostri il tuo codice invece di descriverlo. Includi il codice JSON minimo (come testo, non uno screenshot) e il codice per riprodurre il problema nella tua domanda e possiamo vedere come può essere migliorato. Ulteriori informazioni su un MCVE .
Frank van Puffelen

Modificato per includere la spiegazione del codice
Big_Mac

Risposte:


124

Aggiornamento: ora trattiamo anche questa domanda in un episodio di AskFirebase .

Il caricamento di molti elementi da Firebase non deve essere lento, poiché puoi convogliare le richieste. Ma il tuo codice lo sta rendendo impossibile, il che porterà effettivamente a prestazioni non ottimali.

Nel tuo codice, richiedi un articolo al server, attendi che quell'articolo ritorni e poi carichi quello successivo. In un diagramma di sequenza semplificato che assomiglia a:

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

In questo scenario stai aspettando 30 volte il tempo di andata e ritorno + 30 volte il tempo necessario per caricare i dati dal disco. Se (per semplicità) diciamo che i viaggi di andata e ritorno impiegano 1 secondo e il caricamento di un elemento da disco richiede anche un secondo che almeno a 30 * (1 + 1) = 60 secondi.

Nelle applicazioni Firebase otterrai prestazioni molto migliori se invii tutte le richieste (o almeno un numero ragionevole di esse) in una volta:

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

Se assumiamo di nuovo un viaggio di andata e ritorno di 1 secondo e 1 secondo di caricamento, stai aspettando 30 * 1 + 1 = 31 secondi.

Quindi: tutte le richieste passano attraverso la stessa connessione. Dato che, l'unica differenza tra get(1), get(2), get(3)ed getAll([1,2,3])è certo overhead per le cornici.

Ho impostato un jsbin per dimostrare il comportamento . Il modello di dati è molto semplice, ma mostra la differenza.

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

Per confronto: il caricamento sequenziale di 64 elementi richiede 3,8 secondi sul mio sistema, mentre il caricamento in pipeline (come fa il client Firebase in modo nativo) richiede 600 ms. I numeri esatti dipenderanno dalla tua connessione (latenza e larghezza di banda), ma la versione pipeline dovrebbe essere sempre molto più veloce.


12
Bello, Puf! Inoltre, concatenare promesse (jQuery.whenAll (), q.all () o Promise.all ()) può essere molto utile qui se hai bisogno di caricare tutti gli elementi, ma vuoi comunque prenderli in parallelo, prima di intraprendere un'azione.
Kato

5
Freddo. Non ci ho nemmeno pensato, anche se l'ho usato. :-)
Frank van Puffelen

2
@FrankvanPuffelen Hai ragione dal punto di vista delle prestazioni, ma cosa succederebbe se una di queste chiamate non fosse tornata a causa di qualsiasi tipo di errore? Come puoi "annullare" il resto delle richieste in sospeso se qualcuno di queste non riesce. In caso di richieste sequenziali, possiamo conoscere in codice ciò che la richiesta non è riuscita. Per favore condividi i tuoi pensieri. Grazie.
Perry

1
" Il metodo Promise.all () [...] rifiuta con il motivo della prima promessa che rifiuta."
pejalo

4
Come possiamo fare Promise.all su Android? Come possiamo caricare tutti i dati in Android
Muhammad chhota
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.