Ускорьте получение сообщений для моего приложения в социальной сети с помощью запроса вместо многократного наблюдения за одним событием

98

У меня есть массив ключей, которые ведут к объектам публикации для моей социальной сети, например / posts / id / (информация о публикации)

Когда я загружаю сообщения, я загружаю / posts / 0, а затем / posts / 1 и т.д., используя этот observeSingleEventOfType(.Value)метод.

Я использую 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
        }
    }
}

Эта рекурсивная функция, по сути, выполняет получение значения ключа i из firebase. Если это 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"
  } ]
Биг Мак
источник
1
Будет намного проще помочь, если вы покажете нам свой код, а не описываете его. Включите минимум JSON (в виде текста, а не снимка экрана) и код для воспроизведения проблемы в вашем вопросе, и мы увидим, как ее можно улучшить. Узнайте больше о MCVE .
Франк ван Пуффелен
Отредактировано, чтобы включить объяснение кода
Big_Mac

Ответы:

125

Обновление: теперь мы также рассмотрим этот вопрос в эпизоде ​​AskFirebase .

Загрузка большого количества элементов из 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 раз больше времени, чем требуется для загрузки данных с диска. Если (для простоты) мы скажем, что обратное обращение занимает 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, чтобы продемонстрировать поведение . Модель данных очень проста, но она демонстрирует разницу.

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 мс. Точные числа будут зависеть от вашего соединения (задержки и пропускной способности), но конвейерная версия всегда должна быть значительно быстрее.

Франк ван Пуффелен
источник
12
Здорово, Пуф! Кроме того, цепочка обещаний (jQuery.whenAll (), q.all () или Promise.all ()) может быть здесь очень удобна, если вам нужно загрузить все элементы, но вы все равно хотите захватить их параллельно, прежде чем предпринимать какие-либо действия.
Kato
5
Прохладно. Даже не подумал об этом, хотя я им пользовался. :-)
Франк ван Пуффелен
2
@FrankvanPuffelen Вы правы с точки зрения производительности, но что, если один из этих вызовов не вернулся из-за какой-либо ошибки? Как можно «отменить» остальные ожидающие запросы, если кто-то из них потерпел неудачу. В случае последовательных запросов мы можем узнать в коде, какой запрос не удался. Пожалуйста, поделитесь своими мыслями. Спасибо.
Перри
1
« Метод Promise.all () [...] отклоняется по причине первого отклоненного обещания».
pejalo
4
Как мы можем сделать Promise.all в android? Как мы можем загрузить все данные в Android
Мухаммад Чхота