Gordon Childs
Gordon Childs

Reputation: 36139

PromiseKit dependent promises result in early deallocation

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)")
    }
}

Upvotes: 0

Views: 1356

Answers (1)

Jeffery Thomas
Jeffery Thomas

Reputation: 42598

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)"
        }
    }
}

Upvotes: 1

Related Questions