如果您在操场上运行它:
import Combine
import Foundation
struct User {
let name: String
}
var didAlreadyImportUsers = false
var importUsers: Future<Bool, Never> {
Future { promise in
sleep(5)
promise(.success(true))
}
}
var fetchUsers: Future<[User], Error> {
Future { promise in
promise(.success([User(name: "John"), User(name: "Jack")]))
}
}
var users: AnyPublisher<[User], Error> {
if didAlreadyImportUsers {
return fetchUsers
.receive(on: DispatchQueue.global(qos: .userInitiated))
.eraseToAnyPublisher()
} else {
return importUsers
.receive(on: DispatchQueue.global(qos: .userInitiated))
.setFailureType(to: Error.self)
.combineLatest(fetchUsers)
.map { $0.1 }
.eraseToAnyPublisher()
}
}
users
.receive(on: DispatchQueue.global(qos: .userInitiated))
.sink(receiveCompletion: { completion in
print(completion)
}, receiveValue: { value in
print(value)
})
print("run")
输出将是:
[User(name: "John"), User(name: "Jack")]
run
finished
但是我期望得到:
run
[User(name: "John"), User(name: "Jack")]
finished
因为接收器应在后台线程中运行代码。我在这里想念的。我是否需要冲洗代码:
sleep(5)
promise(.success(true))
在后台线程中?那么
的目的是什么.receive(on: DispatchQueue.global(qos: .userInitiated))
您的Future会在创建后立即运行,因此在您使用的情况下,一旦访问此属性,便会立即运行:
var importUsers: Future<Bool, Never> {
Future { promise in
sleep(5)
promise(.success(true))
}
}
并且[Future
由于立即运行,这意味着传递给promise的闭包将立即执行,使主线程在继续运行之前先休眠5秒钟。在您的情况下,一旦您访问在主线程上完成的Future
,就会立即创建users
。
receive(on:
影响sink
在其上接收值的线程,而不影响在其上创建值的线程。由于在您调用.sink
时期货已经完成,因此完成和发出的价值将立即交付给sink
。在后台队列上,但仍然立即。
之后,您终于点击了print("run")
行。
如果用此替换sleep(5)
位:
var importUsers: Future<Bool, Never> {
Future { promise in
DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
promise(.success(true))
}
}
}
并对您的订阅代码进行一些小的调整:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var cancellables = Set<AnyCancellable>()
users
.receive(on: DispatchQueue.global(qos: .userInitiated))
.sink(receiveCompletion: { completion in
print(completion)
}, receiveValue: { value in
print(value)
}).store(in: &cancellables)
您将看到输出按预期方式打印,因为最初的将来不会在五秒钟内阻塞主线程。
或者,如果您保持睡眠并像这样订阅,您将看到相同的输出:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var cancellables = Set<AnyCancellable>()
users
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
.sink(receiveCompletion: { completion in
print(completion)
}, receiveValue: { value in
print(value)
}).store(in: &cancellables)
原因是您在后台线程上使用了subscribe
,因此订阅和所有内容都是在主线程上异步设置的,这导致print("run")
在接收Future
的结果之前运行。但是,一旦访问users
属性(位于主线程上),主线程仍会睡5秒钟,因为那是在初始化Future
时。