// // WhenAll.swift // then // // Created by Sacha Durand Saint Omer on 08/04/16. // Copyright © 2016 s4cha. All rights reserved. // import Foundation import Dispatch public class Promises {} extension Promises { public static func whenAll(_ promises: [Promise], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return reduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in result.append(element) } } public static func whenAll(_ promises: Promise..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return whenAll(promises, callbackQueue: callbackQueue) } public static func lazyWhenAll(_ promises: [Promise], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return lazyReduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in result.append(element) } } public static func lazyWhenAll(_ promises: Promise..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return lazyWhenAll(promises, callbackQueue: callbackQueue) } // Array version public static func whenAll(_ promises: [Promise<[T]>], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return reduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in result.append(contentsOf: element) } } public static func whenAll(_ promises: Promise<[T]>..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return whenAll(promises, callbackQueue: callbackQueue) } public static func lazyWhenAll(_ promises: [Promise<[T]>], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return lazyReduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in result.append(contentsOf: element) } } public static func lazyWhenAll( _ promises: Promise<[T]>..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> { return lazyWhenAll(promises, callbackQueue: callbackQueue) } // Private implementations private static func lazyReduceWhenAll( _ promises: [Promise], callbackQueue: DispatchQueue?, updatePartialResult: @escaping (_ result: inout [Result], _ element: Source) -> Void) -> Promise<[Result]> { return Promise { fulfill, reject in reducePromises( promises, callbackQueue: callbackQueue, fulfill: fulfill, reject: reject, updatePartialResult: updatePartialResult) } } private static func reduceWhenAll( _ promises: [Promise], callbackQueue: DispatchQueue?, updatePartialResult: @escaping (_ result: inout [Result], _ element: Source) -> Void) -> Promise<[Result]> { let p = Promise<[Result]>() reducePromises( promises, callbackQueue: callbackQueue, fulfill: p.fulfill, reject: p.reject, updatePartialResult: updatePartialResult) return p } private static func reducePromises( _ promises: [Promise], callbackQueue: DispatchQueue?, fulfill: @escaping ([Result]) -> Void, reject: @escaping (Error) -> Void, updatePartialResult: @escaping (_ result: inout [Result], _ element: Source) -> Void) { let ts = ArrayContainer() var error: Error? let group = DispatchGroup() for p in promises { group.enter() p.then { element in updatePartialResult(&ts.array, element) } .onError { error = $0 } .finally { group.leave() } } let callingQueue = OperationQueue.current?.underlyingQueue let queue = callbackQueue ?? callingQueue ?? DispatchQueue.main group.notify(queue: queue) { if let e = error { reject(e) } else { fulfill(ts.array) } } } private class ArrayContainer { private var _array: [T] = [] private let lockQueue = DispatchQueue(label: "com.freshOS.then.whenAll.lockQueue", qos: .userInitiated) var array: [T] { get { return lockQueue.sync { _array } } set { lockQueue.sync { _array = newValue } } } } }