Tăng tốc độ tìm nạp các bài đăng cho ứng dụng mạng xã hội của tôi bằng cách sử dụng truy vấn thay vì quan sát một sự kiện liên tục


97

Tôi có một loạt các khóa dẫn đến đăng các đối tượng cho mạng xã hội của mình như vậy / posts / id / (post info)

Khi tôi tải các bài viết, tôi tải / posts / 0 và sau đó là / posts / 1 etc bằng observeSingleEventOfType(.Value)phương pháp này.

Tôi sử dụng a lazyTableViewđể tải 30 cùng một lúc và nó khá chậm. Có cách nào tôi có thể sử dụng một trong các phương thức truy vấn hoặc một cách khác để làm cho nó nhanh hơn ngay cả khi tôi phải cấu trúc lại dữ liệu trong cây JSON của mình.

Tôi đến từ Phân tích cú pháp để triển khai lại ứng dụng của mình và cho đến nay trải nghiệm này khá tốt. Chỉ một điều này tôi hơi bị mắc kẹt. Xin được cảm ơn trước về sự giúp đỡ!

BIÊN TẬP:

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

Về cơ bản, hàm đệ quy này chạy lấy giá trị cho khóa số i từ firebase. Nếu đó là NSNULL, nó biết rằng đó là bài viết cuối cùng có thể tải và không bao giờ lặp lại. Nếu NSNULL không nhận được lần truy cập nhưng i % 29 == 0sau đó nó trả về như một trường hợp cơ sở, do đó chỉ có 30 bài đăng được tải cùng một lúc (0 được lập chỉ mục). Khi tôi đặt doneLoadingthành true, tableView.reloadData()được gọi bằng trình quan sát thuộc tính.

Đây là ví dụ về mảng mà tôi đang tìm nạp trông như thế nào

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

1
Sẽ dễ dàng hơn rất nhiều để trợ giúp nếu bạn cho chúng tôi xem mã của bạn thay vì mô tả nó. Bao gồm JSON tối thiểu (dưới dạng văn bản, không phải ảnh chụp màn hình) và mã để tái tạo vấn đề trong câu hỏi của bạn và chúng tôi có thể xem cách nó có thể được cải thiện. Đọc thêm về MCVE .
Frank van Puffelen

Đã chỉnh sửa để bao gồm giải thích mã
Big_Mac

Câu trả lời:


123

Cập nhật: bây giờ chúng tôi cũng đề cập đến câu hỏi này trong một tập AskFirebase .

Việc tải nhiều mục từ Firebase không nhất thiết phải chậm, vì bạn có thể chuyển các yêu cầu. Nhưng mã của bạn đang biến điều này thành không thể, điều này thực sự sẽ dẫn đến hiệu suất dưới mức tối ưu.

Trong mã của bạn, bạn yêu cầu một mục từ máy chủ, đợi mục đó trả lại và sau đó tải mục tiếp theo. Trong một sơ đồ trình tự đơn giản trông giống như:

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

Trong trường hợp này, bạn đang đợi gấp 30 lần thời gian khứ hồi + 30 lần thời gian cần để tải dữ liệu từ đĩa. Nếu (để đơn giản) chúng ta nói rằng các vòng quay mất 1 giây và tải một mục từ đĩa cũng mất một giây thì ít nhất là 30 * (1 + 1) = 60 giây.

Trong các ứng dụng Firebase, bạn sẽ nhận được hiệu suất tốt hơn nhiều nếu bạn gửi tất cả các yêu cầu (hoặc ít nhất là một số lượng hợp lý trong số chúng) trong một lần:

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

Nếu chúng tôi một lần nữa giả định là 1 giây khứ hồi và 1 giây tải, bạn đang đợi 30 * 1 + 1 = 31 giây.

Vì vậy: tất cả các yêu cầu đều đi qua cùng một kết nối. Cho rằng, sự khác biệt duy nhất giữa get(1), get(2), get(3)getAll([1,2,3])là số nguyên cần thiết cho khung.

Tôi thiết lập một jsbin để chứng minh hành vi . Mô hình dữ liệu rất đơn giản, nhưng nó cho thấy sự khác biệt.

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

Để so sánh: việc tải tuần tự 64 mục mất 3,8 giây trên hệ thống của tôi, trong khi tải chúng theo chuỗi (như ứng dụng khách Firebase thực hiện nguyên bản) mất 600 mili giây. Các con số chính xác sẽ phụ thuộc vào kết nối của bạn (độ trễ và băng thông), nhưng phiên bản pipelined luôn phải nhanh hơn đáng kể.


12
Tốt, Puf! Ngoài ra, chuỗi chuỗi hứa hẹn (jQuery.whenAll (), q.all () hoặc Promise.all ()) có thể rất hữu ích ở đây nếu bạn cần tải tất cả các mục nhưng vẫn muốn lấy chúng song song trước khi thực hiện một số hành động.
Kato

5
Mát mẻ. Tôi thậm chí không nghĩ đến điều đó, mặc dù tôi đã sử dụng nó. :-)
Frank van Puffelen

2
@FrankvanPuffelen Bạn nói đúng từ quan điểm hiệu suất nhưng nếu một trong những cuộc gọi này không trả lại do bất kỳ loại lỗi nào thì sao? Làm thế nào bạn có thể 'hủy bỏ' phần còn lại của các yêu cầu đang chờ xử lý nếu bất kỳ ai trong số này không thành công. Trong trường hợp yêu cầu tuần tự, chúng tôi có thể biết bằng mã rằng yêu cầu nào không thành công. Hãy chia sẻ suy nghĩ của bạn. Cảm ơn.
Perry

1
" Phương thức Promise.all () [...] từ chối với lý do là lời hứa đầu tiên từ chối."
pejalo

4
Làm thế nào chúng ta có thể thực hiện Promise.all trong Android? Làm thế nào chúng ta có thể tải tất cả các dữ liệu trong android
Muhammad Chhota
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.