0

I'm trying to tame some callback hell pyramid of doom code using PromiseKit.

To do this I wrap my asynchronous code in promises, but depending on how I return dependent promises, I encounter problems. If I unwrap the promise and fulfill/reject then all is well, though more verbose than I'd like. If I return a new dependent promise, then I get an early allocation and the promises are silently broken.

I realise this might not be idiomatic PromiseKit, which appears to be

{ a }.then { b }.then { c }  // in temporal order, take that, callbacks!

but as part of this work it's convenient for me to refactor with functions Promise<A> -> Promise<B>, and I don't understand why I must unwrap at every step. Does anyone know?

Here is some simplified code that reproduces the problem. Try to imagine that there's a good reason that badStringFromInt can't fulfil immediately.

func badStringFromInt(_ intPromise: Promise<Int>) -> Promise<String> {
    return Promise { _, reject in
        intPromise.then { i -> Promise<String> in
            return Promise { fulfill, _ in
                fulfill("\(i)")
            }
        }.catch { error in
            reject(error)
        }
    }
}

func goodStringFromInt(_ intPromise: Promise<Int>) -> Promise<String> {
    return Promise { fulfill, reject in
        intPromise.then { i in
            fulfill("\(i)")
        }.catch { error in
            reject(error)
        }
    }
}

func getInt() -> Promise<Int> {
    return Promise{ fulfill, reject in
        fulfill(5)
    }
}

func doIt() {
    // "then" never called, and this is logged:
    // PromiseKit: Pending Promise deallocated! This is usually a bug
    badStringFromInt(getInt()).then { s in
        print("bad string is :" + s)
    }.catch { error in
        print("error: \(error)")
    }

    // all good!
    goodStringFromInt(getInt()).then { s in
        print("good string is :" + s)
    }.catch { error in
        print("error: \(error)")
    }
}
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159

1 Answers1

1

I must be missing something. Why not just add on to the chain? What does creating the intermediate promise do for you?

betterStringFromInt waits for intPromise to fulfill, then returns a string promise.

func betterStringFromInt(_ intPromise: Promise<Int>) -> Promise<String> {
    return intPromise.then { i in
        DispatchQueue.main.promise {
            return "\(i)"
        }
    }
}
Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • No, I was missing something. I didn't realise `next` returned `Promise`, so now I return that and let the error handling happen further up the chain. I still don't understand the deallocation problem, but the unnecessary `Promise` wrappers are gone now so I don't really need to. Thank you! – Rhythmic Fistman Apr 18 '17 at 05:22