从同步和异步闭包中捕获值

Capturing values from synchronous and asynchronous closures

假设一个像这样的简单结构:

struct Foo {
    let value: Int
    let delay: TimeInterval
}

它有2个函数,每个函数都以一个闭包作为参数。一个是同步调用的,另一个是在delay:

之后异步调用的
extension Foo {
    func sync(_ completion: (Int) -> Void) {
        completion(value)
    }
}

extension Foo {
    func async(_ completion: @escaping (Int) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(delay)) {
            completion(self.value)
        }
    }
}

现在假设我有一个 Foo 对象数组:

let foo1 = Foo(value: 1, delay: 1)
let foo2 = Foo(value: 2, delay: 0)

现在我想查询它们中的每一个以获取它们将提供的值并将它们放入一个数组中。同步的话,可以这样做:

let syncValues = [foo1, foo2].reduce(into: []) { values, foo in foo.sync { values.append([=13=]) } } // [1, 2]

异步,我想这样做并在 foo1 之前让 foo2 报告:

let asyncValues = [foo1, foo2].reduce(into: []) { values, foo in foo.async { values.append([=14=]) }  // [2, 1]}

但是,当我尝试执行异步版本时出现错误: Escaping closure captures 'inout' parameter 'values'

执行此异步任务的好方法是什么?

因为它是异步的,所以不能在单个同步语句中执行此操作。假设您理解这一点,使用 Combine 可能是最简单的:

import Combine

let fooResultsPublishers = [foo1, foo2].publisher
    .flatMap { foo in
        Future { promise in
           foo.async { promise(.success([=10=])) }
        }
    }
    .collect()

要真正获取值,您需要使用.sink对其进行异步操作:

fooResultsPublishers
    .sink {
       print([=11=]) // [2,1]
    }
    // store it in long-living property, like
    // var cancellables: Set<AnyCancellable> = []
    .store(in: &cancellables) 

如果没有 Combine,您需要使用类似 DispatchGroup:

的东西
let group = DispatchGroup()
var values: [Int] = []
[foo1, foo2].forEach { foo in
    group.enter()
    foo.async { values.append([=12=]); group.leave() }
}

group.notify(queue: .main) {
    print(values) // [2,1]
}