当前位置: 首页 > 面试题库 >

从功能返回之前,等待Firebase加载

谢奇略
2023-03-14
问题内容

我有一个简单的功能,可以从Firebase加载数据。

func loadFromFireBase() -> Array<Song>? {
    var songArray:Array<Song> = []

    ref.observe(.value, with: { snapshot in
        //Load songArray
    })

    if songArray.isEmpty {
        return nil
    }
    return songArray
}

当前nil,即使有要加载的数据,此函数也会始终返回。之所以这样做,是因为它永远不会执行执行完成块,在函数返回之前,它不会在数组中加载数组。我正在寻找一种使函数仅在调用完成块后才返回的方法,但不能将return放在完成块中。


问题答案:

(关于此问题的变化经常出现在SO上。我永远找不到一个好的,全面的答案,因此下面尝试提供这样的答案)

你不能那样做。Firebase是异步的。其功能采用完成处理程序并立即返回。您需要重写loadFromFirebase函数以获取完成处理程序。

我在Github上有一个名为 Async_demo
(link)的示例项目,它是一个正在运行的(Swift 3)应用程序,说明了该技术。

其中的关键部分是function downloadFileAtURL,它需要一个完成处理程序并进行异步下载:

typealias DataClosure = (Data?, Error?) -> Void

/**
 This class is a trivial example of a class that handles async processing. It offers a single function, `downloadFileAtURL()`
 */
class DownloadManager: NSObject {

  static var downloadManager = DownloadManager()

  private lazy var session: URLSession = {
    return URLSession.shared
  }()

    /**
     This function demonstrates handling an async task.
     - Parameter url The url to download
     - Parameter completion: A completion handler to execute once the download is finished
     */

      func downloadFileAtURL(_ url: URL, completion: @escaping DataClosure) {

        //We create a URLRequest that does not allow caching so you can see the download take place
        let request = URLRequest(url: url,
                                 cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
                                 timeoutInterval: 30.0)
        let dataTask = URLSession.shared.dataTask(with: request) {
          //------------------------------------------
          //This is the completion handler, which runs LATER,
          //after downloadFileAtURL has returned.
          data, response, error in

          //Perform the completion handler on the main thread
          DispatchQueue.main.async() {
            //Call the copmletion handler that was passed to us
            completion(data, error)
          }
          //------------------------------------------
        }
        dataTask.resume()

        //When we get here the data task will NOT have completed yet!
      }
    }

上面的代码使用Apple的URLSession类从异步服务器异步下载数据。创建时dataTask,您传入一个完成处理程序,该处理程序在数据任务完成(或失败)时被调用。但是请注意:您的完成处理程序在后台线程上被调用。

很好,因为如果您需要执行诸如解析大型JSON或XML结构之类的耗时的处理,则可以在完成处理程序中进行处理而不会导致应用程序的UI冻结。但是,结果是,如果不将这些UI调用发送到主线程,就无法在数据任务完成处理程序中进行UI调用。上面的代码使用调用在主线程上调用了整个完成处理程序DispatchQueue.main.async(){}

返回OP的代码:

我发现以闭包作为参数的函数很难读取,因此我通常将闭包定义为类型别名。

通过@ Raghav7890的答案重做代码以使用typealias:

typealias SongArrayClosure = (Array<Song>?) -> Void

func loadFromFireBase(completionHandler: @escaping SongArrayClosure) {
    ref.observe(.value, with: { snapshot in
        var songArray:Array<Song> = []
        //Put code here to load songArray from the FireBase returned data

        if songArray.isEmpty {
            completionHandler(nil)
        }else {
            completionHandler(songArray)
        }
    })
}

我已经很久没有使用Firebase了(然后只修改了别人的Firebase项目),所以我不记得它是在主线程还是在后台线程上调用它的完成处理程序。如果它在后台线程上调用完成处理程序,那么您可能希望将对完成处理程序的调用包装在对主线程的GCD调用中。

编辑:

根据 这个SO问题的答案,听起来Firebase确实在后台线程上进行网络调用,但是在主线程上调用了侦听器。

在那种情况下,您可以忽略以下有关Firebase的代码,但是对于那些阅读此线程以寻求其他种类的异步代码帮助的人,可以按照以下方法重写代码以在主线程上调用完成处理程序:

typealias SongArrayClosure = (Array<Song>?) -> Void

func loadFromFireBase(completionHandler:@escaping SongArrayClosure) {
    ref.observe(.value, with: { snapshot in
        var songArray:Array<Song> = []
        //Put code here to load songArray from the FireBase returned data

        //Pass songArray to the completion handler on the main thread.
        DispatchQueue.main.async() {
          if songArray.isEmpty {
            completionHandler(nil)
          }else {
            completionHandler(songArray)
          }
        }
    })
}


 类似资料:
  • 问题内容: 我有以下 我有一个运行一些ajax的函数,然后根据ajax是否成功返回true或false。我从代码的多个位置调用了这个ajax函数。 因为该函数在ajax完成之前结束,所以它总是返回false。如何避免这种情况? 我读到一些建议我在函数中执行的操作,然后将和函数移至我的和函数。但是,在我的方法中,我进行了大量的计算。很多代码。因此,问题在于,如果我将功能移至其他功能,那么我将复制一堆

  • 我使用的是Firebase Cloud Firestore,然而,我认为这可能更像是JavaScript异步与同步promise返回的问题。 我正在执行一个查询以从一个集合中获取ID,然后循环该查询的结果以根据该ID从另一个集合中查找单个记录。 我怎么能等到forEach完成后才从外部promise和外部函数本身返回呢?

  • 下面是我正在尝试使用firebase云功能所做的事情: -监听“用户”集合下的文档中的任何更改。 -更新“评论”和“发布”集合中相关文档中用户信息的副本。 因为我将需要在相关文档中进行查询并立即更新,所以我正在编写事务操作的代码。 这是我写的代码。它返回错误消息“Function returned undefined,expected Promise or value”。 我有点困惑,因为据我所知

  • 我正在使用Spring Webflow R2DBC将一些数据插入数据库。 要求提供数据- 控制器 服务 道 主要问题是我不知道如何让它等待所有结果返回并添加到最终

  • 问题内容: 我正在做一些单元测试。测试框架将页面加载到iFrame中,然后对该页面运行声明。在每个测试开始之前,我创建一个,将iFrame的事件设置为call ,设置iFrame的事件,并返回promise。 因此,我可以调用,它将在执行任何操作之前等待页面加载。 我在测试中的所有地方都使用了这种模式(不只是用于加载URL),主要是为了允许对DOM进行更改(例如,点击按钮,然后等待divs隐藏和显