2

我进行了很多搜索以了解其他人是如何解决这个问题的,但不幸的是,我没有找到这个特定问题的答案。我将衷心感谢您的帮助。

总结一下:我的类中有两个方法,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。有人可以将完成块添加到此代码中,因为我在使其工作时遇到问题吗?

4

2 回答 2

1

根据您的代码,您似乎可以控制异步调度。

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

在 Grand Central Dispatch 中使用 dispatch_sync

但是,如果您无法控制异步调用,并且您实际上是在调用对象上的一个方法,然后该对象会调用dispatch_async; 然后您别无选择,如您所说的那样使用完成块、回调模式或信号量

于 2016-05-03T17:27:56.530 回答
1

所以,如果我正确理解了你的问题,你就会有一个“项目”列表和一个异步任务。该任务接受一个参数item并计算一些“结果”(一个整数数组)。

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

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

好吧,当然可以使用调度组、完成处理程序、NSOperations 等来实现这一点。但这很快就会变得非常复杂且容易出错,特别是如果您想处理错误并可能在需要时实现一种取消任务的方法。但由于使用“Futures”这变得非常简单,我将提出这个解决方案。

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

利用期货,该任务具有以下签名:

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.
    }
}
于 2016-05-03T19:57:49.340 回答