Created
June 11, 2014 06:05
-
-
Save akisute/92a46bf65f92f9d4442a to your computer and use it in GitHub Desktop.
Promise in Swift, but completely broken in chaining. I can't just simply implement promise chaining X(
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
enum PromiseState:Int { | |
case Incomplete = 0 | |
case Rejected = 1 | |
case Resolved = 2 | |
} | |
class Promise : Hashable { | |
typealias __BoundBlock = (Void)->(Void) | |
typealias __TransformBlock = (AnyObject?)->(AnyObject?) | |
typealias OnResolvedCallback = (AnyObject?)->(Promise?) | |
typealias OnRejectedCallback = (NSError?)->(Promise?) | |
class func __defaultOnResolvedHandler(result:AnyObject?)->(Promise?) { return nil } | |
class func __defaultOnRejectedHandler(reason:NSError?)->(Promise?) { return nil } | |
var __callbackBindings:Array<__BoundBlock> = [] | |
var __queue:dispatch_queue_t? = nil | |
var __state:PromiseState = .Incomplete | |
let __stateLock:NSLock = NSLock() | |
var __result:AnyObject? = nil | |
var __reason:NSError? = nil | |
var isResolved:Bool { | |
get { | |
return self.__state == .Resolved | |
} | |
} | |
var isRejected:Bool { | |
get { | |
return self.__state == .Rejected | |
} | |
} | |
class func resolved(result:AnyObject?) -> Promise { | |
let deferred = Deferred() | |
deferred.resolve(result) | |
return deferred.promise() | |
} | |
class func rejected(reason:NSError?) -> Promise { | |
let deferred = Deferred() | |
deferred.reject(reason) | |
return deferred.promise() | |
} | |
class func all(promises:Array<Promise>) -> Promise { | |
return AllDeferred(promises:promises) | |
} | |
class func any(promises:Array<Promise>) -> Promise { | |
return AnyDeferred(promises:promises) | |
} | |
init(queue:dispatch_queue_t?=nil) { | |
self.__queue = queue | |
} | |
func then(onResolved:OnResolvedCallback=Promise.__defaultOnResolvedHandler) -> (Promise) { | |
// XXX: Queue will not be chained. Must specify queue using on() explicitly every time if needed. | |
let deferred = Deferred() | |
self.__bindOrCallBlock({[unowned self] in | |
if self.isResolved { | |
if let promise = onResolved(self.__result) { | |
promise.__chainTo(deferred) | |
} else { | |
deferred.resolve(self.__result) | |
} | |
} | |
}) | |
return deferred | |
} | |
func catch(onRejected:OnRejectedCallback=Promise.__defaultOnRejectedHandler) -> (Promise) { | |
// XXX: Queue will not be chained. Must specify queue using on() explicitly every time if needed. | |
let deferred = Deferred() | |
self.__bindOrCallBlock({[unowned self] in | |
if self.isRejected { | |
if let promise = onRejected(self.__reason) { | |
promise.__chainTo(deferred) | |
} else { | |
deferred.reject(self.__reason) | |
} | |
} | |
}) | |
return deferred | |
} | |
func on(queue:dispatch_queue_t) -> (Promise) { | |
let deferred = Deferred(queue:queue) | |
self.__chainTo(deferred) | |
return deferred; | |
} | |
func onMainQueue() -> (Promise) { | |
return self.on(dispatch_get_main_queue()) | |
} | |
func __executeBlock(block:__BoundBlock) { | |
if let queue = self.__queue { | |
dispatch_async(queue, block); | |
} else { | |
block(); | |
} | |
} | |
func __bindOrCallBlock(block:__BoundBlock) -> Bool { | |
var blockWasBound = false | |
self.__stateLock.lock() | |
if self.__state == .Incomplete { | |
self.__callbackBindings += block | |
blockWasBound = true | |
} | |
self.__stateLock.unlock() | |
if !blockWasBound { | |
self.__executeBlock(block) | |
} | |
return blockWasBound | |
} | |
func __chainTo(deferred:Deferred) { | |
if self.isResolved { | |
deferred.resolve(self.__result) | |
} else if self.isRejected { | |
deferred.reject(self.__reason) | |
} else { | |
// I hate this formatting, Xcode :( | |
self.then({result in | |
deferred.resolve(result) | |
}).catch({reason in | |
deferred.reject(reason) | |
}) | |
} | |
} | |
// MARK: - Hashable | |
var hashValue: Int { | |
get { | |
return ObjectIdentifier(self).hashValue | |
} | |
} | |
} | |
// MARK: - Equatable for Promise | |
@infix func == (lhs:Promise, rhs:Promise) -> Bool { | |
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs) | |
} | |
@infix func != (lhs:Promise, rhs:Promise) -> Bool { | |
return !(lhs == rhs) | |
} | |
class Deferred : Promise { | |
func promise() -> Promise { | |
return self | |
} | |
func resolve(result:AnyObject?) -> Promise { | |
self.__result = result | |
self.__transitionToState(.Resolved) | |
return self.promise() | |
} | |
func reject(reason:NSError?) -> Promise { | |
self.__reason = reason | |
self.__transitionToState(.Rejected) | |
return self.promise() | |
} | |
func __transitionToState(state:PromiseState) { | |
var blocksToExecute:Array<__BoundBlock> = [] | |
var shouldComplete = false | |
self.__stateLock.lock() | |
if self.__state == .Incomplete { | |
self.__state = state | |
shouldComplete = true | |
blocksToExecute = self.__callbackBindings | |
self.__callbackBindings.removeAll() | |
} | |
self.__stateLock.unlock() | |
if shouldComplete { | |
for block:__BoundBlock in blocksToExecute { | |
self.__executeBlock(block) | |
} | |
} | |
} | |
} | |
class AllDeferred : Deferred { | |
var promises:Array<Promise> | |
var results:Dictionary<Promise, AnyObject?> | |
init(queue:dispatch_queue_t?=nil, promises:Array<Promise>) { | |
self.promises = promises | |
self.results = [:] | |
super.init(queue:queue) | |
for promise in promises { | |
promise.then({[unowned self] result in | |
self.results[promise] = result | |
if self.promises.count == self.results.count { | |
self.resolve(self.results) // Using dict instead of array (usual implementation)... I think it's fine | |
} | |
return nil | |
}).catch({[unowned self] reason in | |
self.reject(reason) | |
return nil | |
}) | |
} | |
} | |
} | |
class AnyDeferred : Deferred { | |
var promises:Array<Promise> | |
var reasons:Dictionary<Promise, NSError?> | |
init(queue:dispatch_queue_t?=nil, promises:Array<Promise>) { | |
self.promises = promises | |
self.reasons = [:] | |
super.init(queue:queue) | |
for promise in promises { | |
promise.then({[unowned self] result in | |
self.resolve(result) | |
return nil | |
}).catch({[unowned self] reason in | |
self.reasons[promise] = reason | |
if self.promises.count == self.reasons.count { | |
self.reject(nil) | |
} | |
return nil | |
}) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment