I showed how to extend the default Q.promise.timeout method with callback functions in the previous blog post.
In this blog post I will show how to extend promises returned by the
AngularJs $q service with timeout
method. Because angular uses a lite version of the Q
library, the promises returned when you call $q.defer()
have no timeout method at all.
Why would you need to have a separate method just to detect promises that never resolved? To simplify debugging and performance reporting mostly. For example, we could report every ajax request that took longer than 10 seconds to Sentry
1 | $http(url) |
We need to modify the promise object returned by $q.defer()
call.
AngularJs' $providers
are the way to do this, as shown in
Extending $q promises by
@wejendorp.
First, let us create a module that will decorate the $q service.
1 | angular.module('ng-q-timeout', []) |
Second, let us use the .decorate
method to change the $q
1 | .config(['$provide', function ($provide) { |
We have wrapped the original $q.defer
inside newDefer function,
and added timeout
method to the promise object before returning it to the calling code.
All we need to do now is to actually set a timeout and call the user supplied callback.
1 | d.promise.timeout = function (ms, callback) { |
The pending
variable keeps track if the promise has been resolved (or rejected), because
we do not want to signal the timeout if the promise has already been resolved. Please see
the source code
how it is set.
The user's callback is then responsible for two actions:
1: Rejecting the promise if desired and handling the rejection inside the catch
callback
1 | .timeout(1000, function (defer) { |
2: We are using native setTimeout
function, which is not integrated with the angular's
event loop (unlike $timeout but we
cannot inject $timeout into .config
).
If you do any model modifications inside the callback, you need to kick off the $digest
cycle by calling $scope.$apply
1 | .timeout(1000, function (defer) { |
Full implementation including tests is at bahmutov/angular-q-timeout and is available on bower as ng-q-timeout.
limitation
- Each
.then
function returns a different promise object. We only add thetimeout
method to the original promise created in$q.defer()
, so if you want a time out, it has to be the first call on the promise.