You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
4.6 KiB

  1. //
  2. // WhenAll.swift
  3. // then
  4. //
  5. // Created by Sacha Durand Saint Omer on 08/04/16.
  6. // Copyright © 2016 s4cha. All rights reserved.
  7. //
  8. import Foundation
  9. import Dispatch
  10. public class Promises {}
  11. extension Promises {
  12. public static func whenAll<T>(_ promises: [Promise<T>], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  13. return reduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in
  14. result.append(element)
  15. }
  16. }
  17. public static func whenAll<T>(_ promises: Promise<T>..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  18. return whenAll(promises, callbackQueue: callbackQueue)
  19. }
  20. public static func lazyWhenAll<T>(_ promises: [Promise<T>], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  21. return lazyReduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in
  22. result.append(element)
  23. }
  24. }
  25. public static func lazyWhenAll<T>(_ promises: Promise<T>..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  26. return lazyWhenAll(promises, callbackQueue: callbackQueue)
  27. }
  28. // Array version
  29. public static func whenAll<T>(_ promises: [Promise<[T]>], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  30. return reduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in
  31. result.append(contentsOf: element)
  32. }
  33. }
  34. public static func whenAll<T>(_ promises: Promise<[T]>..., callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  35. return whenAll(promises, callbackQueue: callbackQueue)
  36. }
  37. public static func lazyWhenAll<T>(_ promises: [Promise<[T]>], callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  38. return lazyReduceWhenAll(promises, callbackQueue: callbackQueue) { (result, element) in
  39. result.append(contentsOf: element)
  40. }
  41. }
  42. public static func lazyWhenAll<T>(
  43. _ promises: Promise<[T]>...,
  44. callbackQueue: DispatchQueue? = nil) -> Promise<[T]> {
  45. return lazyWhenAll(promises, callbackQueue: callbackQueue)
  46. }
  47. // Private implementations
  48. private static func lazyReduceWhenAll<Result, Source>(
  49. _ promises: [Promise<Source>],
  50. callbackQueue: DispatchQueue?,
  51. updatePartialResult: @escaping (_ result: inout [Result], _ element: Source) -> Void) -> Promise<[Result]> {
  52. return Promise { fulfill, reject in
  53. reducePromises(
  54. promises,
  55. callbackQueue: callbackQueue,
  56. fulfill: fulfill,
  57. reject: reject,
  58. updatePartialResult: updatePartialResult)
  59. }
  60. }
  61. private static func reduceWhenAll<Result, Source>(
  62. _ promises: [Promise<Source>],
  63. callbackQueue: DispatchQueue?,
  64. updatePartialResult: @escaping (_ result: inout [Result], _ element: Source) -> Void) -> Promise<[Result]> {
  65. let p = Promise<[Result]>()
  66. reducePromises(
  67. promises,
  68. callbackQueue: callbackQueue,
  69. fulfill: p.fulfill,
  70. reject: p.reject,
  71. updatePartialResult: updatePartialResult)
  72. return p
  73. }
  74. private static func reducePromises<Result, Source>(
  75. _ promises: [Promise<Source>],
  76. callbackQueue: DispatchQueue?,
  77. fulfill: @escaping ([Result]) -> Void,
  78. reject: @escaping (Error) -> Void,
  79. updatePartialResult: @escaping (_ result: inout [Result], _ element: Source) -> Void) {
  80. let ts = ArrayContainer<Result>()
  81. var error: Error?
  82. let group = DispatchGroup()
  83. for p in promises {
  84. group.enter()
  85. p.then { element in
  86. updatePartialResult(&ts.array, element)
  87. }
  88. .onError { error = $0 }
  89. .finally { group.leave() }
  90. }
  91. let callingQueue = OperationQueue.current?.underlyingQueue
  92. let queue = callbackQueue ?? callingQueue ?? DispatchQueue.main
  93. group.notify(queue: queue) {
  94. if let e = error {
  95. reject(e)
  96. } else {
  97. fulfill(ts.array)
  98. }
  99. }
  100. }
  101. private class ArrayContainer<T> {
  102. private var _array: [T] = []
  103. private let lockQueue = DispatchQueue(label: "com.freshOS.then.whenAll.lockQueue", qos: .userInitiated)
  104. var array: [T] {
  105. get {
  106. return lockQueue.sync {
  107. _array
  108. }
  109. }
  110. set {
  111. lockQueue.sync {
  112. _array = newValue
  113. }
  114. }
  115. }
  116. }
  117. }