89 lines
2.5 KiB
Swift
89 lines
2.5 KiB
Swift
//
|
|
// Copyright 2024 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public final class Future<Value> {
|
|
public typealias ResultType = Swift.Result<Value, Error>
|
|
|
|
private let lock = UnfairLock()
|
|
private var resultUnsynchronized: ResultType?
|
|
private var observersUnsynchronized = [(ResultType) -> Void]()
|
|
|
|
public init() {}
|
|
|
|
public convenience init(value: Value) {
|
|
self.init()
|
|
self.resultUnsynchronized = .success(value)
|
|
}
|
|
|
|
public convenience init(error: Error) {
|
|
self.init()
|
|
self.resultUnsynchronized = .failure(error)
|
|
}
|
|
|
|
public func observe(on scheduler: Scheduler? = nil, block: @escaping (ResultType) -> Void) {
|
|
lock.withLock {
|
|
func execute(_ result: ResultType) {
|
|
// If a scheduler is not specified, try and run on the main
|
|
// queue. Eventually we'll want to switch this default,
|
|
// but for now it matches the behavior we expect from
|
|
// PromiseKit.
|
|
(scheduler ?? DispatchQueue.main).asyncIfNecessary {
|
|
block(result)
|
|
}
|
|
}
|
|
|
|
if let result = resultUnsynchronized {
|
|
execute(result)
|
|
return
|
|
}
|
|
observersUnsynchronized.append(execute)
|
|
}
|
|
}
|
|
private func sealResult(_ result: ResultType) {
|
|
let observers: [(ResultType) -> Void] = lock.withLock {
|
|
guard self.resultUnsynchronized == nil else { return [] }
|
|
self.resultUnsynchronized = result
|
|
|
|
let observers = observersUnsynchronized
|
|
observersUnsynchronized.removeAll()
|
|
return observers
|
|
}
|
|
|
|
observers.forEach { $0(result) }
|
|
}
|
|
|
|
public func resolve(_ value: Value) {
|
|
sealResult(.success(value))
|
|
}
|
|
|
|
public func resolve<T: Thenable>(
|
|
on scheduler: Scheduler? = nil,
|
|
with thenable: T
|
|
) where T.Value == Value {
|
|
thenable.done(on: scheduler) { value in
|
|
self.sealResult(.success(value))
|
|
}.catch(on: scheduler) { error in
|
|
self.sealResult(.failure(error))
|
|
}
|
|
}
|
|
|
|
public func reject(_ error: Error) {
|
|
sealResult(.failure(error))
|
|
}
|
|
|
|
public var result: ResultType? {
|
|
return lock.withLock { self.resultUnsynchronized }
|
|
}
|
|
|
|
public var isSealed: Bool {
|
|
return lock.withLock { self.resultUnsynchronized != nil }
|
|
}
|
|
}
|
|
|
|
public extension Future where Value == Void {
|
|
func resolve() { resolve(()) }
|
|
}
|