// // Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation // MARK: - Fulfilled public extension Thenable { static func when( on scheduler: Scheduler? = nil, fulfilled thenables: [T] ) -> Promise<[Value]> where T.Value == Value { _when(on: scheduler, fulfilled: thenables).map(on: scheduler) { thenables.compactMap { $0.value } } } static func when( on scheduler: Scheduler? = nil, fulfilled tt: T, _ tu: U ) -> Promise<(T.Value, U.Value)> where T.Value == Value { Guarantee._when(on: scheduler, fulfilled: [ tt.asVoid(on: scheduler), tu.asVoid(on: scheduler) ]).map(on: scheduler) { (tt.value!, tu.value!) } } static func when( on scheduler: Scheduler? = nil, fulfilled tt: T, _ tu: U, _ tv: V ) -> Promise<(T.Value, U.Value, V.Value)> where T.Value == Value { Guarantee._when(on: scheduler, fulfilled: [ tt.asVoid(on: scheduler), tu.asVoid(on: scheduler), tv.asVoid(on: scheduler) ]).map(on: scheduler) { (tt.value!, tu.value!, tv.value!) } } static func when( on scheduler: Scheduler? = nil, fulfilled tt: T, _ tu: U, _ tv: V, _ tw: W ) -> Promise<(T.Value, U.Value, V.Value, W.Value)> where T.Value == Value { Guarantee._when(on: scheduler, fulfilled: [ tt.asVoid(on: scheduler), tu.asVoid(on: scheduler), tv.asVoid(on: scheduler), tw.asVoid(on: scheduler) ]).map(on: scheduler) { (tt.value!, tu.value!, tv.value!, tw.value!) } } } public extension Thenable where Value == Void { static func when( on scheduler: Scheduler? = nil, fulfilled thenables: T... ) -> Promise { _when(on: scheduler, fulfilled: thenables) } static func when( on scheduler: Scheduler? = nil, fulfilled thenables: [T] ) -> Promise { _when(on: scheduler, fulfilled: thenables) } } fileprivate extension Thenable { static func _when( on scheduler: Scheduler?, fulfilled thenables: [T] ) -> Promise { guard !thenables.isEmpty else { return Promise.value(()) } let (returnPromise, future) = Promise.pending() let remainingCount = AtomicValue(thenables.count, lock: .init()) for thenable in thenables { thenable.observe(on: scheduler) { result in switch result { case .success: if remainingCount.update(block: { $0 -= 1; return $0 }) == 0 { future.resolve() } case .failure(let error): future.reject(error) } } } return returnPromise } } // MARK: - Resolved public extension Thenable { static func when( on scheduler: Scheduler? = nil, resolved thenables: T... ) -> Guarantee<[Result]> where T.Value == Value { when(on: scheduler, resolved: thenables) } static func when( on scheduler: Scheduler? = nil, resolved thenables: [T] ) -> Guarantee<[Result]> where T.Value == Value { _when(on: scheduler, resolved: thenables).map(on: scheduler) { thenables.compactMap { $0.result } } } } public extension Thenable where Value == Void { } fileprivate extension Thenable { static func _when( on scheduler: Scheduler?, resolved thenables: [T] ) -> Guarantee { guard !thenables.isEmpty else { return Guarantee.value(()) } let (returnGuarantee, future) = Guarantee.pending() let remainingCount = AtomicValue(thenables.count, lock: .init()) for thenable in thenables { thenable.observe(on: scheduler) { _ in if remainingCount.update(block: { $0 -= 1; return $0 }) == 0 { future.resolve() } } } return returnGuarantee } }