等待 Parse Async 函数在 Swift 中完成

Posted

技术标签:

【中文标题】等待 Parse Async 函数在 Swift 中完成【英文标题】:Wait for Parse Async functions to complete in Swift 【发布时间】:2016-02-11 00:57:02 【问题描述】:

我正在尝试等待 Swift 中的 Parse 异步函数重新加载我的 UITableView

我不确定 Completion Handler 在这种情况下是否有用。或调度异步。

我真的很困惑!有人可以帮忙解决这个问题吗

var posts = [PFObject]()
for post in posts 
    post.fetchInBackground()

tableView.reloadData() // I want to execute that when the async functions have finished execution

【问题讨论】:

【参考方案1】:

您想使用 fetchAllInBackground:Block 我在循环中启动一堆解析调用时遇到问题,返回所有这些调用所需的时间比预期的要长。 fetch documentation

它应该看起来像这样:

PFObject.fetchAllInBackground(posts, block:  (complete, error) in
    if (error == nil && complete) 
        self.tableView.reloadData()
    
)

需要注意的一点是,在您的示例中,帖子是空的,并且是一个通用的 PFObject。我假设这只是示例。否则,如果您想在 Parse 中获取所有帖子(而不是更新当前帖子),您将需要使用 PFQuery 而不是获取。 query documentation

【讨论】:

【参考方案2】:

您需要使用fetchInBackgroundWithBlock。或者,如果您想等到所有内容都加载完毕后再更新 UI,请使用 PFObject 的+fetchAllInBackground:block:。请注意,这是一个类方法,因此将被称为PFObject.fetchAllInBackground(...。请参阅文档here。

无论哪种方式,由于您在后台线程中运行,因此您必须在主线程上更新 UI。这通常使用dispatch_async 完成。

另外需要注意的是,如果您在循环中运行fetchInBackgroundWithBlock 并将所有结果收集到一个数组中,那么数组不是线程安全的。您将不得不使用类似dispatch_barrier 或您自己的同步队列来同步对数组的访问。第二个选项的代码如下:

// Declared once and shared by each call (set your own name)...
let queue = dispatch_queue_create("my.own.queue", nil)

// For each call...
dispatch_sync(queue) 
    self.myArray.append(myElement)

【讨论】:

即使我使用 fetchInBackgroundWithBlock 也无济于事,只要我希望完成所有这些以更新我的 UI 数据 真的吗?通常,您会在每个加载时逐步更新 UI。 我使用它的方式我不需要将self.tableView.reloadData() 分派到主队列。我从我的viewDidLoad() func 调用fetchData(),所以如果你从其他地方调用它,你可能需要将self.tableView.reloadData 调度到主队列 没有fetchAllinBackground,能否举个dispatch_barrier的例子 fetchAllInBackground 来自文档 - 我将包含链接。(注意它是大写的-i)【参考方案3】:

这是我为帮助协调异步进程而创建的一个小类:

class CompletionBlock

  var completionCode:()->()

  init?(_ execute:()->() )
   completionCode = execute 

  func deferred() 

  deinit
   completionCode() 

诀窍是在最后一个异步块之后使用要执行的代码创建 CompletionBlock 的实例,并引用闭包内的对象。

let reloadTable = CompletionBlock( self.tableView.reloadData() )
var posts = [PFObject]()
for post in posts 

    post.fetchInBackground() reloadTable.deferred() 

在最后一次捕获超出范围之前,对象将保持“活动”状态。然后对象本身将超出范围,此时将调用其 deinit 执行您的终结代码。

【讨论】:

【参考方案4】:

这是一个使用fetchInBackgroundWithBlock 的示例,它在完成时重新加载tableView

    var myArray = [String]()
func fetchData() 
    let userQuery: PFQuery = PFUser.query()!

    userQuery.findObjectsInBackgroundWithBlock(
        (users, error) -> Void in

        var userData = users!

        if error == nil 
            if userData.count >= 1 
                for i in 0...users!.count-1 
                    self.myArray.append(userData[i].valueForKey("dataColumnInParse") as! String)
                
            
            self.tableView.reloadData()
         else 
            print(error)
        
    )

我的示例是对用户类的查询,但您明白了...

【讨论】:

是否保证完成处理程序会在主线程上执行? 我不确定是否有保证。 如果完成处理程序在主线程以外的任何其他线程上执行,那么您的解决方案可能会崩溃:表格视图尝试访问self.myArray 以呈现单元格,同时修改了相同的数组由另一个线程。【参考方案5】:

我已经对这些块进行了一些实验,它们似乎在主线程上被调用,这意味着可以在那里进行任何 UI 更改。我用来测试的代码如下所示:

func reloadPosts() 
  PFObject.fetchAllIfNeededInBackground(posts)  
    [unowned self] (result, error) in

    if let err = error 
      self.displayError(err)
     

    self.tableView.reloadData()
  

如果您不确定是否在主线程上调用了该块,您可以使用NSThread 类来检查这一点

print(NSThread.currentThread().isMainThread)

如果你想让它防弹,你可以将你的reloadData 包裹在dispatch_block_t 中以确保它在主线程上

编辑: 如果块在主线程上执行,文档没有说明任何地方,但source code 很清楚它确实如此

+ (void)fetchAllIfNeededInBackground:(NSArray *)objects block:(PFArrayResultBlock)block 
    [[self fetchAllIfNeededInBackground:objects] thenCallBackOnMainThreadAsync:block];

【讨论】:

以上是关于等待 Parse Async 函数在 Swift 中完成的主要内容,如果未能解决你的问题,请参考以下文章

Swift 5.5 async let - 错误:表达式为“异步”但未标记为“等待”

Swift Async - Task.detached 在哪个线程上运行?

在 Vuejs 中使用 async-await 等待函数内部的赋值

如何在 JS(节点)中使用回调来等待 Async 函数完成后再继续?

Swift之深入解析异步函数async/await的使用与运行机制

Swift 中的 C# 异步等待