我甚至不确定这是否可能,但我想我无论如何都应该问!
我有一个";UserDefaults";客户和";活的"/"mock";其接口的实现(取自TCA示例,但这并不重要(。
我想让mock实现像实际实现一样(这样就可以读取/写入值(,但不需要实际持久化。
这是我的代码(为游乐场准备(:
import Foundation
import PlaygroundSupport
// Interface - this can't change
public struct UserDefaultsClient {
public var boolForKey: @Sendable (String) -> Bool
public var setBool: @Sendable (Bool, String) async -> Void
}
// Live implementation (ignore if you want, just for reference)
extension UserDefaultsClient {
public static let live: Self = {
let defaults = { UserDefaults(suiteName: "group.testing")! }
return Self(
boolForKey: { defaults().bool(forKey: $0) },
setBool: { defaults().set($0, forKey: $1) }
)
}()
}
// Mock implementation
extension UserDefaultsClient {
static let mock: Self = {
let storage = Storage()
return Self(
boolForKey: { key in
// ❌ Compiler error: Cannot pass function of type '@Sendable (String) async -> Bool' to parameter expecting synchronous function type
return await storage.getBool(for: key)
// return false
},
setBool: { boolValue, key in
await storage.setBool(boolValue, for: key)
}
)
}()
}
// Manages a storage of Bools, mapped to a key
actor Storage {
var storage: [String: Bool] = [:]
func setBool(_ bool: Bool, for key: String) async {
storage[key] = bool
}
func getBool(for key: String) -> Bool {
storage[key] ?? false
}
}
let client: UserDefaultsClient = .live
Task {
client.boolForKey("key")
await client.setBool(true, "key")
client.boolForKey("key")
}
PlaygroundPage.current.needsIndefiniteExecution = true
我试着在调用storage.getBool
时使用Task
,但也不起作用。
有什么想法吗?
tl;dr答案是否定的,没有办法既维护boolForKey
是同步的接口,又让它执行异步操作,同时维护使async/await有价值的模型。
您正在询问如何同步运行异步任务。这有点像问如何制作干水。你违背了asyc/await范式及其基元的基本性质。
使用DispatchSemaphore
之类的同步原语或类似的东西,您可能能够完成您的任务,但您将围绕系统工作。你无法保证未来的兼容性。你可能会引入微妙而有害的虫子。
例如,考虑一下您的actor中的某个调用路径是否能够返回并调用boolForKey
。那可能会陷入僵局。