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
Future enhancements for timeouts and cancellation #106
Comments
Cool stuff, Bruno! May I suggest/request some API tweaks?
Cool stuff again! Looking forward to it. |
Thanks for the feedback. Regarding the default behavior, I was more focused on workflow scenarios where you will usually want to distinguish three outcomes: 1) call returns normally, 2) call times out, 3) call fails for other reasons and I thought that returning a special value like But on the other hand, there are also lots of scenarios where you don't do anything special with timeouts and you'd want them to be treated like any other exception. So throwing by default makes sense too. It also makes the API safer and aligned on lots of other APIs that throw on timeout.
I also like I will then limit the short form (passing the timeout millis directly as a number argument) to 1 argument: I chose So thanks. This is very useful feedback. |
Great! Happy to help as always. I think wrapping string values of |
I made the changes and cleaned up the implementation. It now passes slightly more complex unit tests in the 3 modes (callbacks, fibers and generators). I could easily add a |
Mind explaining what pausing and resuming would do / what it means, given that the actual operation wouldn't be paused/resumed? Without knowing the answer, I'll toss out that it may be good enough just to have cancel — parity with native timers/intervals. (Edit: even canceling doesn't seem super necessary to me, given that again, unlike timers, the actual async operations can't be canceled. Just guarding for timeouts is helpful though!) |
The scenario here is macro operations that execute several low level I/O operations underneath. If you cancel/pause a macro operation, the low level I/O operations that are currently in progress won't be interrupted but subsequent I/O operations (their callbacks) will be cancelled/queued. When you resume the macro operation the queued operations will be resumed. The simplest case is: function foo(_) {
setTimeout(_, 1000);
setTimeout(_, 1000);
}
var fut = foo(); if you pause fut after 500ms, the first |
Ah, thanks for explaining! I haven't personally come across a need for this, but if you or others have, makes sense. If not, might be worth holding off until a need arises. =) |
I don't really have a need for it right now but I'd rather do it while I have my head at it. It should not introduce any runtime overhead and it falls logically into the current code. It might be a nice feature for workflows (suspending a task and resuming it later). My current need was more for |
Moved this experiment to an advanced_futures branch because I should be able to do something more clever with generators. Master and v0.4 are now identical and do not contain this experimental API. |
The advanced_futures branch appears to be identical to master right now? Anyways, here is some quick coffee code for anyone who needs a timeout in a hurry:
You can call this with:
|
I should point out, you can also call this with:
Or, in other words, you can turn a regular future into a future with a timeout, and then multiple callers can all wait on |
There is actually a Regarding the "advanced_futures" branch being an ancestor of the current master, this is because I did not manage it properly as a feature branch. Instead, I kept developing new features in this branch for a while. Then it was easier to merge it into master and remove "advanced" code than to cherry-pick all the other commits done on this branch. I wanted to be a bit more ambitious with this branch and give the ability to cancel a future, or suspend/resume it. But this has a bit of overhead and it makes the whole thing more complex. So I gave up for now. I can always resurrect it later from the last commit before this branch got merged into master. |
just looked at callWithTimeout, makes sense. I have something similar i had built locally too. It would be slick though if the timeout somehow flowed down to any nested async calls that the top level function is making. For example, if the top level function does steps A, B, C in sequence, and when the timeout fires it is in the middle of step A, it would be great if B and C don't get run. Right now, all the remaining nested work still runs. But i realize this is a bit tricky and probably has a perf impact. Perhaps this is equivalent to the idea of cancelling a future that you were persuing a while back Bruno. |
Wow @bjouhier, just discovered You should document it! |
This experiment is in an old branch. I'm dropping this: it is intellectually challenging but it adds a lot of complexity and nobody really asked for it; |
Futures should support timeouts and cancellation.
I have prototyped this in the
futures
branch. It needs more unit tests and debugging but the basics are working.The proposed API consists in allowing an optional
timeout
argument when waiting on a future. This optional argument is a small object with the following fields:millis
: number of milliseconds that the future will wait (>= 0)value
: the value returned on timeout. Ifthrows
is true and ifvalue != null
thenvalue
will be thrown.throws
: if true, the future will throw on timeout, otherwise it will returnvalue
.probe
: if true, the future is only probed and continues to run if a timeout occurs. Otherwise it is cancelled upon timeout.Note: node APIs aren't cancellable so cancellation means that the callbacks of the pending operations initiated by the future will be inhibited. Integration with true cancellable APIs is for future study.
Here is an example:
As a shortcut, the
millis
,value
,throws
andprobe
values may be passed directly as separate arguments (in this order). Then:I would also like to introduce a
flows.race(futures, count)
function in theflows
module. This function would return thecount
fastest results of an array of futures, as an array of results (count
is optional and defaults to 1).Together with
flows.collect(futures)
and the proposed timeout feature, this should provide a reasonable basis for workflows.The text was updated successfully, but these errors were encountered: