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

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&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 来完成这个特定的任务,但我会牢记这一点!

以上是关于如何让一个方法在继续执行之前等待另一个(异步)方法完成?的主要内容,如果未能解决你的问题,请参考以下文章

如何让另一个异步调用等待?

线程和异步的分别与联系

如何在做其他事情之前返回许多 Promise 并等待它们

可可触摸:在继续之前等待方法完成

在继续之前等待一个功能完成的正确方法?

多线程