ios 为什么@autoclosure @escaping在异步函数上不起作用以及如何修复它

uidvcgyl  于 2023-03-20  发布在  iOS
关注(0)|答案(1)|浏览(104)

我有一个异步函数队列,如下所示:

class Queue {
    var queue: [() async -> Void] = []
    
    func enqueue(_ operation: @escaping () async -> Void) {
        queue.append(operation)
    }
}

你可以这样使用它:

func networkCall() async {
    Task.sleep(2)
}

let queue = Queue()
queue.enque(networkCall)

我想用类似的方式来尝试对一个带有如下参数的函数进行enque:queue.enque(networkCall(duration: 2))
为此,我必须将@autoclosure属性添加到enqueue(_:)函数中,如下所示:

func enqueue(_ operation: @autoclosure @escaping () async -> Void) {
    // ...
}

但这导致了一个错误:'async' autoclosure parameter in a non-'async' function .
我的问题是,为什么会出现这个错误?自动闭包也在转义,所以它不会在enque函数中执行,那么为什么编译器会在意它没有标记为async?我还想知道是否有任何修复方法,以便为我的enqueue函数实现类似queue.enque(networkCall(duration: 2))的函数调用。
先谢了。

jgwigjjp

jgwigjjp1#

当然,将转义的异步自动闭包作为非异步函数的参数在技术上并没有什么问题,但Swift仍然会因为“风格”原因而阻止您这样做。
想想你会如何称呼enqueue,你 * 不能 * 用你喜欢的方式称呼它:

queue.enqueue(networkCall(duration: 2))

相反,您需要执行以下操作:

queue.enqueue(await networkCall(duration: 2))

因为Swift Concurrency的基本原则之一是所有挂起点都用await标记,就像每个可能发生错误的地方都用try标记一样。
尝试实际地使enqueue异步,并亲自查看它-您必须编写

await queue.enqueue(await networkCall(duration: 2))

因此,如果enqueue不是异步的,那么这意味着类似queue.enqueue(await networkCall(duration: 2))的东西可能会出现在非异步代码中。**您最终会看到await * 似乎 * 出现在非异步代码中。**当然,这在本质上是不正确的,但它仍然令人惊讶,需要读者查看enqueue的定义,才能看到它实际上是一个自动闭包。
事实上,some people argue@autoclosure根本不应该与@escaping一起使用(没有任何异步操作),因为自动创建的闭包可能会意外地捕获self并创建一个保留循环。
我建议使用非自动闭包,语法只是(){}的问题:

queue.enqueue { await networkCall(duration: 2) }

无论是否取消此限制,您都需要await

相关问题