단일 이벤트를 반복적으로 관찰하는 대신 쿼리를 사용하여 내 소셜 네트워크 앱의 게시물 가져 오기 속도를 높입니다.


98

나는 그렇게 / posts / id / (post info)와 같은 내 소셜 네트워크에 대한 게시 개체로 이어지는 일련의 키를 가지고 있습니다.

게시물을로드 할 때 observeSingleEventOfType(.Value)메소드를 사용하여 / posts / 0을로드 한 다음 / posts / 1 등을로드합니다 .

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

이 재귀 함수는 기본적으로 firebase에서 키 번호 i의 값을 가져 오는 데 실행됩니다. NSNULL이면로드 할 수있는 마지막 게시물임을 알고 다시는 수행하지 않습니다. NSNULL이 적중되지 않으면 i % 29 == 0기본 케이스로 반환되므로 한 번에 30 개의 게시물 만로드됩니다 (인덱싱 된 0 개). 내가 설정 한 경우 doneLoadingtrue, 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 2016 년

코드 설명을 포함하도록 수정 됨
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 배 + 디스크에서 데이터를로드하는 데 걸리는 시간의 30 배를 기다리고 있습니다. (간단 함을 위해) 왕복에 1 초가 걸리고 디스크에서 항목을로드하는데도 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 클라이언트가 기본적으로 수행하는 것처럼) 600ms가 걸립니다. 정확한 숫자는 연결 (대기 시간 및 대역폭)에 따라 다르지만 파이프 라인 버전은 항상 훨씬 더 빠릅니다.


12
좋아, 퍼프! 또한 모든 항목을로드해야하지만 일부 조치를 취하기 전에 병렬로 가져 가고자하는 경우 연결 약속 (jQuery.whenAll (), q.all () 또는 Promise.all ())이 여기에서 매우 편리 할 수 ​​있습니다.
Kato

5
멋있는. 내가 그것을 사용하고 있지만 그것을 생각조차하지 않았습니다. :-)
Frank van Puffelen 2011 년

2
@FrankvanPuffelen 성능 관점에서 옳았지만 이러한 호출 중 하나가 오류 유형으로 인해 반환되지 않으면 어떻게 될까요? 이 중 누군가가 실패한 경우 대기중인 나머지 요청을 어떻게 '취소'할 수 있습니까? 순차적 요청의 경우 어떤 요청이 실패했는지 코드에서 알 수 있습니다. 여러분의 생각을 공유 해주세요. 감사.
Perry

1
" Promise.all () 메서드 [...]는 거부하는 첫 번째 promise의 이유로 거부합니다."
pejalo

4
Android에서 Promise.all을 어떻게 할 수 있습니까? 우리는 안드로이드의 모든 데이터를로드 할 수있는 방법
무하마드 chhota
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.