Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

feat($debounce): add new service that "debounces" calls to a function #6749

Closed
wants to merge 6 commits into from

Conversation

petebacondarwin
Copy link
Member

This is a useful service in general but, in particular, it also is needed
to support the ng-model-options directive, that allows users to "debounce"
the ui event triggers that update the ng-model value.

See #2129

This is a useful service in general but, in particular, it also is needed
to support the `ng-model-options` directive, that allows users to "debounce"
the ui event triggers that update the ng-model value.
@mary-poppins
Copy link

Thanks for the PR! Please check the items below to help us merge this faster. See the contributing docs for more information.

  • Uses the issue template (#6749)

If you need to make changes to your pull request, you can update the commit with git commit --amend.
Then, update the pull request with git push -f.

Thanks again for your help!

expect(counter).toBe(2);
}));

it('should return a promise which will be resolved with the result of the call to the wrapped function',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some funky indentation in this file in a few places

- Add option to skip $apply
- Only run $apply when the wrapped function is called, and not when we
are cancelling timeouts.
-
@lrlopez
Copy link
Contributor

lrlopez commented Mar 19, 2014

Wow! That was fast.

* @param {boolean} [immediate=false] If true then `fn` is invoked on the first call to
* the debounced function. Otherwise the first call will not
* happen until after the `delay` time has expired
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invokeApply=false doesn't make sense if you are exposing a promise based api. we already have a problem with $interval and there is a hack to workaround this in $timeout I think. Aren't you making the same mistake?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that because our $q framework always wraps resolves in $apply blocks? That didn't seem to be the case in my unit testing but then unit tests are not real life with respect to digests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to call $browser.defer.flush() (to emulate Q's nextTick())... the $timeout and $interval mocks cover that, you probably want to add a mock for the $debounce service

Basically this https://github.com/angular/angular.js/blob/master/src/ngMock/angular-mocks.js#L1647-L1649 although in your own tests I think $timeout.flush() should be enough

@IgorMinar
Copy link
Contributor

  • update example to not use immediate because it feels weird
  • do not return promise
  • remove support for invokeApply for now because it's broken due to $timeout.cancel which will trigger apply anyway.

@IgorMinar
Copy link
Contributor

otherwise this looks good

We are not going to expose promises from this service for now since it
is not a common use case and it is easier to add them in later than to
remove them if they cause problems.  Moreover it makes the code considerable
simpler.

The code will implicitly call $apply if it calls the wrapped function
asynchronously, as it is inside the $timeout handler.  The code will
not explicitly call $apply if it is calling the wrapped function
synchronously (i.e. if `immediate` is `true`) - we will leave it up to
the calling environment to control that.  In most cases the caller will
already be in an apply block.
@lefos987 lefos987 added this to the 1.3.0 milestone Mar 20, 2014
@petebacondarwin
Copy link
Member Author

Fixed up those changes requested by @IgorMinar. The code is much much simpler now, easier to follow and will run much faster.

var context = this, args = arguments;

// This is the function that will be called when the delay period is complete
var delayComplete = function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this function meed to be recreated every time the debounced function is called? (it may not matter a whole lot)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I can move it up a level to the $debounce function and pass in context and args.

@petebacondarwin
Copy link
Member Author

Thanks @caitp and @lrlopez for the feedback

Remove unneeded parameters and move delayComplete outside of debouncedFn.
@petebacondarwin
Copy link
Member Author

Fixed up one more time.

@shahata
Copy link
Contributor

shahata commented Mar 21, 2014

@petebacondarwin, please consider:

  1. Have the debounced function return value be the value returned by the wrapped function the last time it was invoked
  2. Adding an option to cancel the current pending debounce

To see a working example of those two points, you can take a look at my debounce service/directive implementation at: https://github.com/shahata/angular-debounce which sadly I wrote last night while not knowing that those features are going to make it into angular so soon :)

@petebacondarwin
Copy link
Member Author

@shahata - nice service you have written! I especially like the comprehensive tests. For our purposes (i.e. for ng-model-options) the two features you suggest are not necessary right now.
Since it is easy to add in features that take away we are going to go initially with the minimal functionality and then if there is a strong use case add such additional features later.
Can I suggest that you wait till this service lands then send in two PRs one for each feature and we can then discuss merging them?

@shahata
Copy link
Contributor

shahata commented Mar 21, 2014

Sure, I totally agree with this approach and I'd be happy send those PRs. Regarding the return value, I thought that you might want to start with it since you chose to support the 'immediate' param (I guess in order to be compatible with the existing debounce implementation in underscore, since I don't think it is an important use case). Regarding the cancel support, you might want to read my comment in #2129. I think it is pretty important...

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants