如何让一个方法在继续执行之前等待另一个(异步)方法完成?
Posted
技术标签:
【中文标题】如何让一个方法在继续执行之前等待另一个(异步)方法完成?【英文标题】:How to make one method wait for another (async) method to finish before continuing execution? 【发布时间】:2016-05-03 17:21:47 【问题描述】:我进行了很多搜索以了解其他人如何解决此问题,但不幸的是,我没有找到此特定问题的答案。非常感谢您的帮助。
以下是摘要: 我的班级有两个方法,method1 和 method2。我必须在 method1 中调用一个异步函数。然后代码继续执行并到达method2。但是在方法 2 中,有些情况我需要在方法 1 中使用异步调用的结果,因此我需要确保方法 1 中的异步调用已经完成,然后再继续方法 2 的其余部分。
我知道一种方法是使用信号量,另一种方法是使用完成块。但我想以最通用的方式执行此操作,因为会有其他方法,类似于方法 2,它们将再次需要等待方法 1 中的异步调用完成才能继续执行。同样出于同样的原因,我不能简单地调用 method2 本身内部的 async 函数并将其余的 method2 放在其完成块中。
这是我想要做的一个粗略的想法。如果有人将完成块添加到这个伪代码中,我将不胜感激,这样我就可以清楚地看到事情是如何工作的。顺便说一句,method1 和 method2(以及此类中的所有其他方法)在同一个线程上(但不是主线程)。
@implementation someClass
-(void)method1
for (each item in the ivar array)
if (condition C is met on that item)
some_independent_async_call(item, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int result, int *someArray)
if (result > 0)
// The async call worked correctly, we will need someArray
else
// We didn't get someArray that we wanted.
);
-(void)method2
// Select one item in the ivar array based on some criteria.
if (condition C is met on that item)
// Wait for some_independent_async_call() in method1 to complete.
// Then use someArray in the rest of the code.
else
// Simply continue the rest of the code.
@end
更新:我知道我可以在异步调用完成后发出信号量,并且我可以在方法 2 中等待相同的信号量,但我想使用完成块,因为我认为这会更通用,特别是如果有其他方法与此类中的方法 2 类似。有人可以在我的代码中添加完成块吗?
【问题讨论】:
您的for
循环逻辑让我认为这可能是您正在寻找的内容:“使用调度组——分组块允许聚合同步。您的应用程序可以提交多个块和跟踪它们何时全部完成,即使它们可能在不同的队列上运行。当在所有指定任务都完成之前无法取得进展时,此行为会很有帮助。"
谢谢,但我只有一个需要完成的块,以及需要使用其结果的多个地方。您能否在上面的伪代码中添加调度组在这种情况下的工作方式?
您的异步调用在循环中。您的意思是“条件 C”仅适用于一项吗?
不,实际上它可能适用于多个项目,我们需要在每个项目上调用该异步方法。稍后,在方法 2 中,我们只选择一个项目,如果它具有条件 C,我们需要从方法 1 中的异步调用中为该特定项目使用 someArray。
好的...这就是我建议组的原因。如果您有多个异步操作正在运行,我假设您希望它们在进入 method2 之前全部完成。
【参考方案1】:
根据您的代码,您似乎可以控制异步调度。
而不是some_independent_async_call
使用dispatch_sync
,它将阻止当前线程上的执行,直到给定块完成
using dispatch_sync in Grand Central Dispatch
但是,如果您无法控制异步调用并且您实际上是在调用对象上的一个方法,然后该对象会调用dispatch_async
;你别无选择,然后使用你所说的完成块、回调模式或信号量
【讨论】:
谢谢。我无法控制异步调用,它只是我需要使用的其他地方的异步方法。你能告诉我如何在这里使用完成块,因为我尝试时似乎没有正确吗? 你应该在method1
的调度块中调用method2
,在块的末尾【参考方案2】:
所以,如果我正确理解了您的问题,您就会有一个“项目”列表和一个 异步 任务。该任务接受一个参数item
并计算一些“结果”(一个整数数组)。
现在,对于每个“项目”,将评估一个布尔值,以确定是否应该以该项目作为参数启动任务。
在完成每个任务(您的some_independent_async_call
)后,您想要调用一个延续(可能使用相应已完成任务的结果)。
当然可以使用调度组、完成处理程序、NSOperations 等来实现这一点。但这很快就会变得非常复杂且容易出错,特别是如果您想处理错误并可能在需要时实现一种取消任务的方法。但由于使用“Futures”这变得非常简单,我将提出这个解决方案。
请注意,“futures”不包含在标准 Swift 库中。但是,有一些第三方库可用。在这里,我将使用"Scala-like" futures。请注意,这也可以用“Promises”以类似的方式实现。另请参阅 wiki Futures and Promises。
利用期货,任务具有以下签名:
typealias TaskType = (Item) -> Future<SomeResult>
未来是一个值的“占位符”,稍后将由底层异步任务计算。该任务也可能失败并返回错误而不是值。也就是说,未来将最终完成结果或错误。
让任务成为:
func task(item: Item) -> Future<SomeResult>
let promise = Promise<SomeResult>()
fetchSomeResult(item.url) (result, error) in
if let result = result
promise.fulfill(result)
else
promise.reject(error!)
return promise.future!
过滤数组以获取启动任务的实际“项目”似乎更容易:
let activeItems = items.filter $0.foo == something
获取期货数组:
let futures = activeItems.map task($0)
上面的语句将启动异步任务,每个任务都有其对应的项目,并返回一个类型为[Future<SomeResult>]
的期货数组。此时,期货尚未完成。当底层任务完成时,这最终会在每个未来发生。
现在,为每个未来添加一个延续,在任务成功时调用它,并添加一个错误处理程序,在发生错误处理程序时调用:
futures.forEach future in
future.map result in
// This will be entered for each task, once it
// finished successfully.
// Handle result (should be your array of ints)
// This is where you implement the "method2" part
// but for each item separately.
// Note: if you need the "item" as well, use a
// task which returns a tuple:
// task(item: Item) -> Future<(Item, SomeResult)>
.onFailure error in
// handle error. This is an error returned from the task.
【讨论】:
感谢您在此代码中详细说明如何使用期货。恐怕我不能使用 Swift 来完成这个特定的任务,但我会牢记这一点!以上是关于如何让一个方法在继续执行之前等待另一个(异步)方法完成?的主要内容,如果未能解决你的问题,请参考以下文章