Przyspiesz pobieranie postów do mojej aplikacji społecznościowej, używając zapytania zamiast wielokrotnego obserwowania pojedynczego zdarzenia

98

Mam tablicę kluczy, które prowadzą do publikowania obiektów w mojej sieci społecznościowej, takich jak: / posts / id / (informacje o wpisie)

Kiedy ładuję posty, ładuję / posts / 0, a następnie / posts / 1 itd., Używając observeSingleEventOfType(.Value)metody.

Używam lazyTableViewdo załadowania 30 na raz i jest to dość wolne. Czy istnieje sposób, w jaki mogę użyć jednej z metod zapytania lub innego sposobu na przyspieszenie, nawet jeśli muszę zmienić strukturę danych w moim drzewie JSON.

Pochodzę z Parse, ponownie wdrażam moją aplikację i jak dotąd doświadczenie jest całkiem dobre. Tylko ta jedna rzecz, na której trochę utknąłem. Z góry dziękuję za pomoc!

EDYTOWAĆ:

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

Ta rekurencyjna funkcja zasadniczo pobiera wartość klucza numer i z firebase. Jeśli jest to NSNULL, wie, że jest to ostatni możliwy do załadowania post i nigdy więcej tego nie robi. Jeśli NSNULL nie zostanie trafiony, ale i % 29 == 0wróci jako przypadek podstawowy, więc tylko 30 postów jest ładowanych na raz (0 indeksowanych). Kiedy ustawiony doneLoadingdo true, tableView.reloadData()nazywany jest przy użyciu właściwości obserwatora.

Oto próbka tego, jak wygląda tablica, którą pobieram

"ideaPosts" : [ {
    "id" : 0,
    "message" : "Test",
    "upvotes" : 1,
    "user" : "Anonymous"
  }, {
    "id" : 1,
    "message" : "Test2",
    "upvotes" : 1,
    "user" : "Anonymous"
  } ]
Big_Mac
źródło
1
O wiele łatwiej będzie Ci pomóc, jeśli pokażesz nam swój kod zamiast go opisywać. Dołącz minimalny kod JSON (jako tekst, a nie zrzut ekranu) i kod, aby odtworzyć problem w swoim pytaniu, a my zobaczymy, jak można to poprawić. Przeczytaj więcej o MCVE .
Frank van Puffelen
Edytowano, aby uwzględnić wyjaśnienie kodu
Big_Mac

Odpowiedzi:

125

Aktualizacja: omawiamy teraz również to pytanie w odcinku AskFirebase .

Ładowanie wielu elementów z Firebase nie musi być powolne, ponieważ możesz potokować żądania. Ale twój kod uniemożliwia to, co rzeczywiście doprowadzi do nieoptymalnej wydajności.

W swoim kodzie żądasz elementu z serwera, czekasz, aż ten element wróci, a następnie załaduj następny. Na uproszczonym diagramie sekwencji, który wygląda następująco:

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

W tym scenariuszu czekasz 30-krotność czasu podróży w obie strony + 30-krotność czasu potrzebnego na załadowanie danych z dysku. Jeśli (dla uproszczenia) powiemy, że podróż w obie strony zajmuje 1 sekundę, a ładowanie elementu z dysku również zajmuje jedną sekundę, co najmniej do 30 * (1 + 1) = 60 sekund.

W aplikacjach Firebase uzyskasz znacznie lepszą wydajność, jeśli wyślesz wszystkie żądania (lub przynajmniej rozsądną ich liczbę) za jednym razem:

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

Jeśli ponownie przyjmiemy 1 sekundę podróży w obie strony i 1 sekundę ładowania, czekasz 30 * 1 + 1 = 31 sekund.

A więc: wszystkie żądania przechodzą przez to samo połączenie. Biorąc pod uwagę, że jedyna różnica między get(1), get(2), get(3)a getAll([1,2,3])pewne napowietrznych do klatek.

Założyłem jsbin wykazać zachowanie . Model danych jest bardzo prosty, ale pokazuje różnicę.

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

Dla porównania: sekwencyjne ładowanie 64 elementów zajmuje w moim systemie 3,8 sekundy, podczas gdy ładowanie ich potokiem (tak jak klient Firebase robi natywnie) zajmuje 600 ms. Dokładne liczby będą zależeć od twojego połączenia (opóźnienia i przepustowości), ale wersja potokowa zawsze powinna być znacznie szybsza.

Frank van Puffelen
źródło
12
Świetnie, Puf! Ponadto łączenie obietnic (jQuery.whenAll (), q.all () lub Promise.all ()) może być tutaj bardzo przydatne, jeśli potrzebujesz załadować wszystkie elementy, ale nadal chcesz je złapać równolegle, zanim podejmiesz jakąś akcję.
Kato
5
Chłodny. Nawet o tym nie pomyślałem, mimo że go używałem. :-)
Frank van Puffelen
2
@FrankvanPuffelen Masz rację z punktu widzenia wydajności, ale co, jeśli jedno z tych połączeń nie powróciło z powodu jakiegokolwiek błędu? W jaki sposób możesz „anulować” pozostałe oczekujące żądania, jeśli którykolwiek z nich się nie powiedzie. W przypadku żądań sekwencyjnych możemy dowiedzieć się w kodzie, które żądanie nie powiodło się. Podziel się swoimi przemyśleniami. Dzięki.
Perry
1
Metoda Promise.all () [...] odrzuca z powodu pierwszej obietnicy, która odrzuca”.
pejalo
4
Jak możemy zrobić Promise.all na Androidzie? Jak możemy załadować wszystkie dane na Androida
Muhammad Chhota