I love asynchronous programming using promises. Q is my favorite promise implementation library. An asynchronous function can return a promise, and I can setup success and fail callbacks
1 | var q = require('q'); |
What if we want to automatically fail if foo
takes too long? Q has method
Q.promise.timeout
that can reject a promise with a given message
1 | foo() |
How do I detect if the promise timed out or really failed? I have to check inside the .fail
callback, making the code more complex:
1 | foo() |
It is very simple to extend or wrap the Q's promise object and extend the timeout
method
and allow a dedicated callback function on time out, not just an error message. I placed the
code into this gist. The main
feature: we are wrapping the Q.defer
function
1 | var _defer = q.defer; |
Now we can run the dedicated callback on timed out promises, which gets the original deferred object
1 | foo() |
limitations
- If we reject the promise (line
// 1
) we need to make sure the.fail
callback can tell the difference between the timed out and failed promise. For example, by rejecting the promise without arguments intimeout
and assuming that every true failure would have arguments. - Each
.then
function returns a different promise object. We only add thetimeout
method to the original promise created inq.defer()
, so if you want a time out, it has to be the first call on the promise.
Update 1
I extended AngularJs $q promises with timeout method, see this post
Update 2
Q library has added promise.timeout method that works just like I described above. Any long-running promise can be limited to a given period. If the promise is not resolved after the period expires, the promise is rejected. This allows to start a normal long running action using an ordinary method, but limit it from the outside.
1 | // "normal" promise-returning function, knows nothing about the time limit |
This produces the following output
at 10 ms: started long-running promise
at 415 ms: promise error with value [Error: timed out]
at 1011 ms: resolving long-running promise with 42 after 1 second
While there is a message at 1011 ms
before the deferred.resolve(42);
is called, the promise
has already been fulfilled. Thus onOk
or onError
is not going to be called the second time.
Important notice that the application DOES NOT EXIT until all promises get resolved (after 1 second).
This is the problem with promises - they keep running and there is no way to get them cancelled without
access to the original deferred
object.
Update 3
Whenever working with asynchronous JavaScript code I like to see a timestamp next to some of the log statements. I find printing just the current time verbose and less than useful. I am more interested in the elapsed time rather than the local time. Thus instead of just using console-stamp I prefer to create a dedicated method that shows elapsed time in milliseconds since the "start".
1 | var started = +(new Date()); |