Skip to content

Commit 186c3d8

Browse files
committed
feat($timeout): Add debouncing support to $timeout service
This feature allows resetting the timer on an ongoing `$timeout` promise. A fourth optional argument is added to `$timeout` which is the old promise to be reset. I.e.: `promise = $timeout(fn, 2000, true, promise);` This will call `fn()` 2 seconds after the last call to the `$timeout()` function.
1 parent 37123cd commit 186c3d8

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

src/ng/timeout.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,23 @@ function $TimeoutProvider() {
2929
* @param {number=} [delay=0] Delay in milliseconds.
3030
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
3131
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
32+
* @param {Promise=} debounce If set to an outgoing promise, it will reject it before creating
33+
* the new one. This allows debouncing the execution of the function.
3234
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
3335
* promise will be resolved with is the return value of the `fn` function.
3436
*/
35-
function timeout(fn, delay, invokeApply) {
37+
function timeout(fn, delay, invokeApply, debounce) {
3638
var deferred = $q.defer(),
3739
promise = deferred.promise,
3840
skipApply = (isDefined(invokeApply) && !invokeApply),
3941
timeoutId, cleanup;
4042

43+
// debouncing support
44+
if (debounce && debounce.$$timeoutId in deferreds) {
45+
deferreds[debounce.$$timeoutId].reject('debounced');
46+
$browser.defer.cancel(debounce.$$timeoutId);
47+
}
48+
4149
timeoutId = $browser.defer(function() {
4250
try {
4351
deferred.resolve(fn());

test/ng/timeoutSpec.js

+30
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,34 @@ describe('$timeout', function() {
199199
expect(cancelSpy).toHaveBeenCalledOnce();
200200
}));
201201
});
202+
203+
204+
describe('debouncing', function() {
205+
it('should allow debouncing tasks', inject(function($timeout) {
206+
var task = jasmine.createSpy('task'),
207+
successtask = jasmine.createSpy('successtask'),
208+
errortask = jasmine.createSpy('errortask'),
209+
promise = null;
210+
211+
promise = $timeout(task, 10000, true, promise);
212+
promise.then(successtask, errortask);
213+
214+
expect(task).not.toHaveBeenCalled();
215+
expect(successtask).not.toHaveBeenCalled();
216+
expect(errortask).not.toHaveBeenCalled();
217+
218+
promise = $timeout(task, 10000, true, promise);
219+
expect(task).not.toHaveBeenCalled();
220+
expect(successtask).not.toHaveBeenCalled();
221+
expect(errortask).not.toHaveBeenCalled();
222+
223+
$timeout.flush();
224+
225+
expect(task).toHaveBeenCalled();
226+
// it's a different promise, so 'successtask' should not be called but 'errortask' should
227+
expect(successtask).not.toHaveBeenCalled();
228+
expect(errortask).toHaveBeenCalled();
229+
}));
230+
231+
});
202232
});

0 commit comments

Comments
 (0)