等待 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 函数完成后再继续?