如何让一个方法在继续执行之前等待另一个(异步)方法完成?

时间:2016-05-03 17:21:48

标签: objective-c asynccallback completion-block

我经常搜索其他人是如何解决这个问题的,但不幸的是,我没有找到这个具体问题的答案。我非常感谢你的帮助。

以下是摘要: 我的类中有两个方法,method1和method2。我必须在method1中调用异步函数。然后代码继续执行并到达method2。但是在method2中,有些情况下我需要在method1中使用该异步调用的结果,所以我需要确保在继续使用方法2的其余部分之前,方法1中的异步调用已经完成。

我知道一种方法是使用信号量,另一种方法是使用完成块。但我想以最通用的方式执行此操作,因为将有其他方法,类似于method2,它将再次需要在继续执行之前等待method1中的异步调用完成。同样出于同样的原因,我不能简单地在method2本身中调用async函数,并将其余的method2放在其完成块中。

这里对我想要做的事情有一个粗略的了解。如果有人将完成块添加到这个伪代码中,我会很感激,所以我可以清楚地看到事情是如何工作的。 BTW,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

更新:我知道一旦异步调用完成,我就可以发出信号量信号,我可以在method2中等待相同的信号量,但是我想使用完成块,因为我认为这样会更通用,尤其是如果有其他的话与此类中的method2类似的方法。有人可以为这段代码添加完成块,因为我在使这项工作有问题吗?

2 个答案:

答案 0 :(得分:1)

基于您的代码,您可以控制异步调度。

而不是some_independent_async_call使用dispatch_sync,它将阻止当前线程的执行,直到给定的块完成

using dispatch_sync in Grand Central Dispatch

但是,如果您无法控制异步调用,并且实际上正在调用某个对象的方法,那么该方法将调用dispatch_async;你没有选择使用完成块,回调模式或信号量,如你所说

答案 1 :(得分:1)

所以,如果我已正确理解你的问题,你就会有一个" Items"和一个异步任务。该任务采用参数item并计算一些"结果" (一系列的Ints)。

现在,对于每个"项目"将评估一个布尔值,该值确定是否应该使用此项作为参数启动任务。

完成每项任务(你的some_independent_async_call)后,你想调用一个延续(可能使用相应完成任务的结果)。

当然可以使用调度组,完成处理程序,NSOperations等来实现它。但是这很快就会变得非常精细且容易出错,特别是如果你想处理错误并且可能在需要时实现取消任务的方法。但是,因为这对于"期货"变得非常简单。我会提出这个解决方案。

请注意"期货"不包含在标准Swift库中。但是,有一些第三方库可用。在这里,我将使用"Scala-like" futures。注意,这可以用" Promises"来实现。也以类似的方式。另请参阅wiki Futures and Promises

利用期货,任务有以下特征:

typealias TaskType = (Item) -> Future<SomeResult>

未来是一个&#34;占位符&#34;对于稍后将由底层异步任务计算的值。任务也可能失败并返回错误而不是值。也就是说,未来将最终完成结果或错误。

让任务成为:

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!
}

过滤数组以获得实际的&#34;项目&#34;似乎更容易。开始一项任务:

let activeItems = items.filter { $0.foo == something }

获得一系列期货:

let futures = activeItems.map { task($0) } 

上述语句将启动异步任务,每个异步任务都有相应的项目,并返回类型为[Future<SomeResult>]的未来数组。目前,期货尚未完成。当底层任务完成时,这将最终发生在每个未来。

现在,为任务成功时调用的每个future添加一个continuation,并添加一个错误处理程序,当一个错误处理程序出现时调用它:

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.
    }
}