TM-SGNL-iOS/SignalServiceKit/PromiseKit/Future.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

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(()) }
}