Skip to content

Instantly share code, notes, and snippets.

@akisute
Created June 11, 2014 06:05
Show Gist options
  • Save akisute/92a46bf65f92f9d4442a to your computer and use it in GitHub Desktop.
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(
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