Acelere a busca de postagens para meu aplicativo de rede social usando consulta em vez de observar um único evento repetidamente

98

Eu tenho uma série de chaves que levam a objetos de postagem para minha rede social, como / posts / id / (informações de postagem)

Quando eu carrego os posts eu carrego / posts / 0 e então / posts / 1 etc usando o observeSingleEventOfType(.Value)método.

Eu uso um lazyTableViewpara carregar 30 de cada vez e é bem lento. Existe alguma maneira de usar um dos métodos de consulta ou outra maneira de torná-lo mais rápido, mesmo se eu tiver que reestruturar os dados em minha árvore JSON.

Estou vindo do Parse reimplementando meu aplicativo e até agora a experiência tem sido muito boa. Só uma coisa em que estou um pouco preso. Obrigado antecipadamente pela ajuda!

EDITAR:

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

Essa função recursiva basicamente executa obtendo o valor da chave número i do firebase. Se for NSNULL, ele saberá que é a última postagem possível para carregar e nunca mais o fará. Se NSNULL não for atingido, i % 29 == 0então ele retorna como um caso base, então apenas 30 posts são carregados por vez (0 indexado). Quando eu configuro doneLoadingpara true, tableView.reloadData()é chamado usando um observador de propriedade.

Aqui está um exemplo de como é a matriz que estou buscando

"ideaPosts" : [ {
    "id" : 0,
    "message" : "Test",
    "upvotes" : 1,
    "user" : "Anonymous"
  }, {
    "id" : 1,
    "message" : "Test2",
    "upvotes" : 1,
    "user" : "Anonymous"
  } ]
Big Mac
fonte
1
Será muito mais fácil ajudar se você nos mostrar seu código em vez de descrevê-lo. Inclua o JSON mínimo (como texto, não uma captura de tela) e código para reproduzir o problema em sua pergunta e podemos ver como pode ser melhorado. Leia mais sobre um MCVE .
Frank van Puffelen
Editado para incluir a explicação do código
Big_Mac

Respostas:

124

Atualização: agora também cobrimos essa questão em um episódio do AskFirebase .

O carregamento de muitos itens do Firebase não precisa ser lento, pois você pode canalizar as solicitações. Mas o seu código está tornando isso impossível, o que certamente levará a um desempenho abaixo do ideal.

Em seu código, você solicita um item do servidor, espera que esse item retorne e carrega o próximo. Em um diagrama de sequência simplificado que se parece com:

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

Neste cenário, você está esperando 30 vezes o tempo de ida e volta + 30 vezes o tempo que leva para carregar os dados do disco. Se (por uma questão de simplicidade) dissermos que as viagens de ida e volta levam 1 segundo e o carregamento de um item do disco também leva um segundo, pelo menos até 30 * (1 + 1) = 60 segundos.

Em aplicativos Firebase, você obterá um desempenho muito melhor se enviar todas as solicitações (ou pelo menos um número razoável delas) de uma vez:

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 assumirmos novamente uma viagem de ida e volta de 1 segundo e 1 segundo de carregamento, você está esperando por 30 * 1 + 1 = 31 segundos.

Portanto: todas as solicitações passam pela mesma conexão. Dado isso, a única diferença entreget(1) , get(2), get(3)e getAll([1,2,3])é uma sobrecarga para os quadros.

Eu configurei um jsbin para demonstrar o comportamento . O modelo de dados é muito simples, mas mostra a diferença.

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

Para comparação: o carregamento sequencial de 64 itens leva 3,8 segundos no meu sistema, enquanto carregá-los em pipeline (como o cliente Firebase faz nativamente) leva 600 ms. Os números exatos dependerão de sua conexão (latência e largura de banda), mas a versão em pipeline deve sempre ser significativamente mais rápida.

Frank van Puffelen
fonte
12
Legal, Puf! Além disso, as promessas de encadeamento (jQuery.whenAll (), q.all () ou Promise.all ()) podem ser muito úteis aqui se você precisar que todos os itens carregados, mas ainda quiser capturá-los em paralelo, antes de realizar alguma ação.
Kato
5
Legal. Nem pensei nisso, embora eu tenha usado. :-)
Frank van Puffelen
2
@FrankvanPuffelen Você está certo do ponto de vista do desempenho, mas e se uma dessas ligações não retornasse devido a algum tipo de erro? Como você pode 'cancelar' o restante dos pedidos pendentes se algum deles falhar. No caso de pedidos sequenciais, podemos saber em código qual foi o pedido que falhou. Por favor, compartilhe seus pensamentos. Obrigado.
Perry
1
" O método Promise.all () rejeita com o motivo da primeira promessa que rejeita."
pejalo
4
Como podemos fazer Promise.all no android? Como podemos carregar todos os dados no Android
Muhammad chhota