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


98

У мене є масив ключів, які ведуть до розміщення об’єктів для моєї соціальної мережі, наприклад so / posts / id / (інформація про публікацію)

Коли я завантажую повідомлення, я завантажую / posts / 0, а потім / posts / 1 тощо, використовуючи observeSingleEventOfType(.Value)метод.

Я використовую a 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 .
Frank van Puffelen,

Відредаговано, щоб включити пояснення коду
Big_Mac,

Відповіді:


124

Оновлення: тепер ми також висвітлюємо це питання в епізоді 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-кратного часу туди-назад + 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 ()) тут можуть бути дуже зручні, якщо вам потрібні всі завантажені елементи, але ви все одно хочете захопити їх паралельно, перш ніж вживати певних дій.
Като

5
Класно. Навіть не думав про це, хоча я цим користувався. :-)
Frank van Puffelen

2
@FrankvanPuffelen Ви маєте рацію з точки зору продуктивності, але що, якби один із цих дзвінків не повернувся через будь-яку помилку? Як ви можете "скасувати" решту запитів, що очікують на розгляд, якщо хтось із них не вдався. У разі послідовних запитів ми можемо дізнатися в коді про те, який запит не вдався. Будь ласка, поділіться своїми думками. Дякую.
Перрі

1
" Метод Promise.all () [...] відхиляє причину першої обіцянки, яка відхиляє."
pejalo

4
Як ми можемо зробити Promise.all в android? Як ми можемо завантажити всі дані в андроїд
Мухаммад Чхота
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.