google/promises

Ignore a certain Error and terminate Promise

kientux opened this issue · 5 comments

Is there any way to completely ignore an Error? For example, when I reject a Promise with something like PromiseCancelledError, I don't want the downstream catches to handle this error. I want the Promise to be terminated and produce no output at all. Both catch and recover are not working for this case.

I was about to say, use recover — can you explain why it's not working for your use-case? Another option might be always?

Breaking the chain like that seems to be against the goals of using promises and the code makes every attempt to not break the promise chain.

Is this a common pattern you need? Why can't the catch block handle it, or some catch along the way can catch it and substitute a more appropriate error?

A promise must always produce a value or an error. So catch is not working like exception catch in Java. In Promise the error will go down to all catches even when you already handled it. If use recover then it will eventually goes to then.

Let say I store a Promise to trigger API request everytime users type to search box. So for every character, I should cancel the previous request. Because Promise does not offer a cancellation mechanism, so I reject the Promise with a PromiseCancelledError.

func doSearch(query: String?) {
    self.promise?.reject(PromiseCancelledError())
    self.promise = doApiRequest(query: query)
}

If I use this only in one place then sure I can catch PromiseCancelledError and just ignore it, or I can use recover and produce a nil value or something to indicate that then shouldn't handle the value. But I want to put this in a base class and use it in several places, then the above approach will need a lot of boilerplate codes (and who uses that class must always be acknowledged about the cancelled error, which I think is not neccessary?)

I know that breaking the chain is not something Promise is designed for, just want to know if it can support my usecase 😁

Best way I can think of is an extension like this:

extension Promise {
    func catchIgnoreCancelled(_ handler: @escaping Catch) -> Promise<Value> {
        self.catch({ error in
            if error is PromiseCancelledError {
                print("promise is cancelled")
            } else {
                handler(error)
            }
        })
    }
}

but every catches must be replace with this.

Hmm, it seems that promise?.reject(...) is not working.

let promise: Promise<String> = Promise { fulfill, reject in
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
        fulfill("done")
    }
}
    .then({ s in
        print(s)
    })
    .catch({ error in
        print(error)
    })

promise.reject(PromiseCancelledError())
print("Promise:", promise.description)

This code block still prints out "done" instead of PromiseCancelledError. Am I doing anything wrong here? The last line even prints out Promise: Rejected: PromiseCancelledError(). Is fulfill suppose to not be called after promise state is rejected?

Even this is not working:

let promise = Promise<String>
    .pending()
    .then({ s in
        print("=========================== then:", s)
    })
    .catch({ error in
        print("=========================== catch:", error)
    })
promise.reject(CustomError())

Nothing is printed out. When I debug into sourcecode, _observers is inserted for then and catch but when call reject, _observers is always nil.