-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Promise chain cancellation #339
Comments
Currently, unless you are using one of the categories that inherently support cancellation (e.g. UIActionSheet), the only supported way to "cancel" a promise chain is from within the chain itself using Again, I haven't tested this, but it sounds like it should work in theory. Let me know how it goes if you try it. |
Actually this won't work. Sorry, I need to think on this some more. |
Yes it's possible. Can't detail code yet. Swift or ObjC? |
Objective-C would be better for me. If you prefer giving an example on Swift, this will also suffice to get the idea how to implement this. |
Any news? This does interest me as well, because I am new to Swift and iOS and looking for an easy way to handle promises as a convenience for multithreading processes that might never need to finish due to the user-initiated changes. I am able to think of workarounds by using out-of-scope variables that the promise checks for termination, but @mxcl 's last comment sounds as if there might be also other possibilities to terminate the block from the outside. |
I am still using PromiseKit and haven't implemented a cancellation yet. |
Alright, sorry for getting back so late on this. Using Swift, you could do what I detailed before and then wrap both the promise from Unfortunately, |
Can we get a code example? For a branching example would be: foo.then {
//…
}.then.then…
foo.then {
//…
}.then.then… Here cancellation would work provided you cancel foo. How do you cancel foo? Like so: foo = bar.then {
throw Error.Cancel
}
enum Error: ErrorType, CancellableErrorType {
case Cancel
} So is this not sufficient? I'm guessing, no. |
Closing pending feedback, please re-open if you want to continue the discussion. |
I'd love to re-open, (sorry I see you just closed this about 12 days ago). I'm looking for a clean solution as such, where the implementor of the Promise (not the issuer) can cancel the Promise. My case is an autocomplete... where a user continues to type new values, if the previous request has not been completed, I want to abort it. var request: Promise<JSON>?
. . .
func valChanged(sender: UITextField) {
if self.request != nil && !self.request.isCancelled {
self.request.cancel()
}
self.request = getNewResults(sender.text)
self.request.then { (response) ->
self.results = response["data"].arrayValue
self.tableView.reloadData()
}
} |
@ded for your usage you need to implement your own cancel method on a subclass of promise. e.g.: class MyPromise: Promise<JSON> {
var mytask
let reject: (ErrorType) -> Void
func cancel() {
mytask.cancel()
reject(NSError.cancelledError)
}
init() {
self.init { fulfill, reject in
self.reject = reject
mytask = MyTask()
mytask.startWithCompletionHandler {
fulfill()
}
}
}
} |
I am trying to solve the same thing, your example above is not compiling because of the self requirement in the init block. Do you have a working solution for this issue maybe? |
public class MyPromise: Promise<JSON> {
private var mytask
private let reject: (ErrorType) -> Void
public func cancel() {
mytask.cancel()
reject(NSError.cancelledError)
}
public func static go() -> MyPromise {
var reject: ((ErrorType) -> Void)!
let promise = MyPromise { fulfill, r in
reject = r
mytask = MyTask()
mytask.startWithCompletionHandler {
fulfill()
}
}
promise.reject = reject
return promise
}
} |
Thank you for the help, I could make it: public class MyPromise: Promise<NSData> {
private var task: NSURLSessionDataTask?
private var fulfill: ((NSData) -> Void)?
private var reject: ((ErrorType) -> Void)?
override init(@noescape resolvers: (fulfill: (NSData) -> Void, reject: (ErrorType) -> Void) throws -> Void) {
super.init(resolvers: resolvers)
}
init(request: NSURLRequest) {
var fulfill: ((NSData) -> Void)?
var reject: ((ErrorType) -> Void)?
super.init { f, r in
fulfill = f
reject = r
}
self.fulfill = fulfill
self.reject = reject
self.task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
if let error = error {
if error.code == NSURLErrorCancelled {
return
}
self.reject?(error)
return
}
guard let data = data else {
self.reject?(NSError(domain: "no-data", code: 0, userInfo: nil))
return
}
self.fulfill?(data)
}
self.task?.resume()
}
public func cancel() {
guard !self.rejected else { return }
self.task?.cancel()
self.reject?(NSError.cancelledError())
}
public class func go() -> MyPromise {
var fulfill: ((NSData) -> Void)?
var reject: ((ErrorType) -> Void)?
let promise = MyPromise { f, r in
fulfill = f
reject = r
}
promise.fulfill = fulfill
promise.reject = reject
let url = NSURL(string: "https://jsonplaceholder.typicode.com/users")!
let request = NSURLRequest(URL: url)
promise.task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
if let error = error {
if error.code == NSURLErrorCancelled {
return
}
promise.reject?(error)
return
}
guard let data = data else {
promise.reject?(NSError(domain: "no-data", code: 0, userInfo: nil))
return
}
promise.fulfill?(data)
}
promise.task?.resume()
return promise
}
} Usage example: let promise = MyPromise.go()
promise
.then { data -> Void in
print("data")
print(data)
}
.error { error in
print("error")
print(error)
}
promise.cancel()
print(promise.rejected) let url = NSURL(string: "https://jsonplaceholder.typicode.com/users")!
let request = NSURLRequest(URL: url)
let promise = MyPromise(request: request)
promise
.then { data -> Void in
print("data")
print(data)
}
.error { error in
print("error")
print(error)
}
promise.cancel()
print(promise.rejected) Just in case if anyone needs something like this. ;) |
months later... thank you for this! i'll have a play with this style. |
could you give some sample in Objective c |
foo.then(^{
@throw NSError.cancelledError;
}) |
@tib Thanks for your code. It helped us solve a similar issue. However, isn't there a potential issue with retain cycles with the way you strongly reference self inside of |
|
Amendment: however if you didn't start the |
I would like to use the above discussed solution but subclassing Promise is not possible (release 6.5) because it's not an |
You don’t need to subclass to use this pattern. |
FYI for everyone we are considering a patch to do the original question in #899 |
Also came here while in need for a cancel-solution. #899 isn't released yet and the detailed approach above doesn't seem to work anymore, because Edit: I guess storing the seal that's handed to |
Is there a possibility to cancel a whole chain of promises (including nested ones), no matter where we are currently in the chain?
Imagine the following case:
We are sealing a promise like
[self syncAllData]
which chains multiple operations - fetching data, parsing it, persisting it and all these operations for multiple entities.At the end imagine that we have a
Cancel
button, that the user can press at any moment during the synchronisation.Is it possible to implement this?
I have checked the current documentation for cancellation, but not able to understand how exactly it can work in my specific case.
The text was updated successfully, but these errors were encountered: