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.

259 lines
7.8 KiB

  1. //
  2. // Promise.swift
  3. // then
  4. //
  5. // Created by Sacha Durand Saint Omer on 06/02/16.
  6. // Copyright © 2016 s4cha. All rights reserved.
  7. //
  8. import Foundation
  9. import Dispatch
  10. private class Locker {
  11. let lockQueueSpecificKey: DispatchSpecificKey<Void>
  12. let lockQueue: DispatchQueue
  13. init() {
  14. lockQueueSpecificKey = DispatchSpecificKey<Void>()
  15. lockQueue = DispatchQueue(label: "com.freshOS.then.lockQueue", qos: .userInitiated)
  16. lockQueue.setSpecific(key: lockQueueSpecificKey, value: ())
  17. }
  18. var isOnLockQueue: Bool {
  19. return DispatchQueue.getSpecific(key: lockQueueSpecificKey) != nil
  20. }
  21. }
  22. public class Promise<T> {
  23. // MARK: - Protected properties
  24. internal var numberOfRetries: UInt = 0
  25. private var threadUnsafeState: PromiseState<T>
  26. private var threadUnsafeBlocks: PromiseBlocks<T> = PromiseBlocks<T>()
  27. private var initialPromiseStart:(() -> Void)?
  28. private var initialPromiseStarted = false
  29. internal typealias ProgressCallBack = (_ resolve: @escaping ((T) -> Void),
  30. _ reject: @escaping ((Error) -> Void),
  31. _ progress: @escaping ((Float) -> Void)) -> Void
  32. private var promiseProgressCallBack: ProgressCallBack?
  33. // MARK: - Lock
  34. private let locker = Locker()
  35. private var lockQueue: DispatchQueue {
  36. return locker.lockQueue
  37. }
  38. private func _synchronize<U>(_ action: () -> U) -> U {
  39. if locker.isOnLockQueue {
  40. return action()
  41. } else {
  42. return lockQueue.sync(execute: action)
  43. }
  44. }
  45. private func _asynchronize(_ action: @escaping () -> Void) {
  46. lockQueue.async(execute: action)
  47. }
  48. // MARK: - Intializers
  49. public init() {
  50. threadUnsafeState = .dormant
  51. }
  52. public init(_ value: T) {
  53. threadUnsafeState = .fulfilled(value: value)
  54. }
  55. public init(error: Error) {
  56. threadUnsafeState = PromiseState.rejected(error: error)
  57. }
  58. public convenience init(callback: @escaping (
  59. _ resolve: @escaping ((T) -> Void),
  60. _ reject: @escaping ((Error) -> Void)) -> Void) {
  61. self.init()
  62. promiseProgressCallBack = { resolve, reject, progress in
  63. callback(resolve, reject)
  64. }
  65. }
  66. public convenience init(callback: @escaping (
  67. _ resolve: @escaping ((T) -> Void),
  68. _ reject: @escaping ((Error) -> Void),
  69. _ progress: @escaping ((Float) -> Void)) -> Void) {
  70. self.init()
  71. promiseProgressCallBack = { resolve, reject, progress in
  72. callback(resolve, reject, progress)
  73. }
  74. }
  75. // MARK: - Private atomic operations
  76. private func _updateFirstPromiseStartFunctionAndState(from startBody: @escaping () -> Void, isStarted: Bool) {
  77. _synchronize {
  78. initialPromiseStart = startBody
  79. initialPromiseStarted = isStarted
  80. }
  81. }
  82. // MARK: - Public interfaces
  83. public func start() {
  84. _synchronize({ return _start() })?()
  85. }
  86. public func fulfill(_ value: T) {
  87. _synchronize({ () -> (() -> Void)? in
  88. let action = _updateState(.fulfilled(value: value))
  89. threadUnsafeBlocks = .init()
  90. promiseProgressCallBack = nil
  91. return action
  92. })?()
  93. }
  94. public func reject(_ anError: Error) {
  95. _synchronize({ () -> (() -> Void)? in
  96. let action = _updateState(.rejected(error: anError))
  97. // Only release callbacks if no retries a registered.
  98. if numberOfRetries == 0 {
  99. threadUnsafeBlocks = .init()
  100. promiseProgressCallBack = nil
  101. }
  102. return action
  103. })?()
  104. }
  105. // MARK: - Internal interfaces
  106. internal func synchronize<U>(
  107. _ action: (_ currentState: PromiseState<T>, _ blocks: inout PromiseBlocks<T>) -> U) -> U {
  108. return _synchronize {
  109. return action(threadUnsafeState, &threadUnsafeBlocks)
  110. }
  111. }
  112. internal func resetState() {
  113. _synchronize {
  114. threadUnsafeState = .dormant
  115. }
  116. }
  117. internal func passAlongFirstPromiseStartFunctionAndStateTo<X>(_ promise: Promise<X>) {
  118. let (startBlock, isStarted) = _synchronize {
  119. return (self.initialPromiseStart ?? self.start, self.initialPromiseStarted)
  120. }
  121. promise._updateFirstPromiseStartFunctionAndState(from: startBlock, isStarted: isStarted)
  122. }
  123. internal func tryStartInitialPromiseAndStartIfneeded() {
  124. var actions: [(() -> Void)?] = []
  125. _synchronize {
  126. actions = [
  127. _startInitialPromiseIfNeeded(),
  128. _start()
  129. ]
  130. }
  131. actions.forEach { $0?() }
  132. }
  133. internal func updateState(_ newState: PromiseState<T>) {
  134. _synchronize({ return _updateState(newState) })?()
  135. }
  136. internal func setProgressCallBack(_ promiseProgressCallBack: @escaping ProgressCallBack) {
  137. _synchronize {
  138. self.promiseProgressCallBack = promiseProgressCallBack
  139. }
  140. }
  141. internal func newLinkedPromise() -> Promise<T> {
  142. let p = Promise<T>()
  143. passAlongFirstPromiseStartFunctionAndStateTo(p)
  144. return p
  145. }
  146. internal func syncStateWithCallBacks(success: @escaping ((T) -> Void),
  147. failure: @escaping ((Error) -> Void),
  148. progress: @escaping ((Float) -> Void)) {
  149. _synchronize {
  150. switch threadUnsafeState {
  151. case let .fulfilled(value):
  152. success(value)
  153. case let .rejected(error):
  154. failure(error)
  155. case .dormant, .pending:
  156. threadUnsafeBlocks.success.append(success)
  157. threadUnsafeBlocks.fail.append(failure)
  158. threadUnsafeBlocks.progress.append(progress)
  159. }
  160. }
  161. }
  162. // MARK: - Private non-atomic operations
  163. private func _startInitialPromiseIfNeeded() -> (() -> Void)? {
  164. guard !initialPromiseStarted else { return nil }
  165. initialPromiseStarted = true
  166. let body = self.initialPromiseStart
  167. return body
  168. }
  169. private func _start() -> (() -> Void)? {
  170. guard threadUnsafeState.isDormant else { return nil }
  171. let updateAction = _updateState(.pending(progress: 0))
  172. guard let p = promiseProgressCallBack else { return updateAction }
  173. return {
  174. updateAction?()
  175. p(self.fulfill, self.reject, self.setProgress)
  176. }
  177. // promiseProgressCallBack = nil //Remove callba
  178. }
  179. private func _updateState(_ newState: PromiseState<T>) -> (() -> Void)? {
  180. if threadUnsafeState.isPendingOrDormant {
  181. threadUnsafeState = newState
  182. }
  183. return launchCallbacksIfNeeded()
  184. }
  185. private func launchCallbacksIfNeeded() -> (() -> Void)? {
  186. switch threadUnsafeState {
  187. case .dormant:
  188. return nil
  189. case .pending(let progress):
  190. if progress != 0 {
  191. return threadUnsafeBlocks.updateProgress(progress)
  192. } else {
  193. return nil
  194. }
  195. case .fulfilled(let value):
  196. initialPromiseStart = nil
  197. return threadUnsafeBlocks.fulfill(value: value)
  198. case .rejected(let anError):
  199. initialPromiseStart = nil
  200. return threadUnsafeBlocks.reject(error: anError)
  201. }
  202. }
  203. }
  204. // MARK: - Helpers
  205. extension Promise {
  206. var isStarted: Bool {
  207. return synchronize { state, _ in
  208. switch state {
  209. case .dormant:
  210. return false
  211. default:
  212. return true
  213. }
  214. }
  215. }
  216. }