【问题标题】: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 之前全部完成。

标签: objective-c asynccallback completion-block


【解决方案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&lt;SomeResult&gt;]的期货数组。此时,期货尚未完成。当底层任务完成时,这最终会在每个未来发生。

现在,为每个未来添加一个延续,在任务成功时调用它,并添加一个错误处理程序,在发生错误处理程序时调用:

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 来完成这个特定的任务,但我会牢记这一点!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-20
  • 1970-01-01
  • 1970-01-01
  • 2014-02-26
相关资源
最近更新 更多