如果你在Swift中使用结构化并发,并且在异步上下文中,编译器会假设如果存在一个异步版本的函数,那就是你想要使用的。有没有办法告诉它你想要同步版本?
下面是一个例子(并表明这至少在理论上是可能的)
在香草SpriteKit中,如果你有一个SKNode和这些行:
node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6))
node.run(SKAction.scale(by: 25, duration: 6))
节点将同时在6秒的时间内旋转和缩放。
…如果您正在使用结构化并发,run
将使用该函数的异步版本和所需的代码:
await node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6))
await node.run(SKAction.scale(by: 25, duration: 6))
将导致节点旋转6秒,然后缩放6秒。completion
回调被异步风格的流控制所取代。
问题是我不想让它等待并返回,但是我不知道有什么方法可以说"嘿——就使用这个函数的同步版本吧"。如果我只是省略await
,这是一个编译错误。
为了证明这是可能的,我可以强制使用函数的同步版本,如下所示:
node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6)){}
node.run(SKAction.scale(by: 25, duration: 6)){}
…提供结束尾随闭包。所以run的同步版本没有什么特别之处。这是有效的,但是没有其他的方法来说"只使用同步功能"吗?
我也可以这样做:
Task{ await node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6))}
Task{ await node.run(SKAction.scale(by: 25, duration: 6)) }
…但这似乎更麻烦。
那么——底线——在Swift中,当你使用结构化并发时,当一个函数同时有同步和异步版本时,你如何表明你想使用同步版本?
在async函数内部,编译器更倾向于使用重载方法的async变体。有几种方法可以说服它使用非异步函数。
假设我们有一个重载函数foo
:
func foo() {}
func foo() async {}
选择同步函数的第一种方法是闭包。在闭包内部,编译器更倾向于调用同步函数:
func barl() async {
{ foo() }()
}
我们还可以使用as
:
func bar2() async {
(foo as () -> _)()
}
为完整起见,我们还可以使用闭包选择同步函数,然后调用闭包获取该函数,然后调用该函数:
func bar3() async {
{ foo }()()
}
我更喜欢第一个解决方案,因为其他两个在调用时丢失了命名参数。我们可以将其推广为一个辅助函数:
func sync<T>(block: () -> T) -> T {
block()
}
使用这个帮助器,我们可以明确要求同步调用:
func bar() async {
sync { foo() }
}