Skip to content
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

Added throttling function #1996

Merged
merged 10 commits into from
Jun 1, 2018
Merged

Added throttling function #1996

merged 10 commits into from
Jun 1, 2018

Conversation

jacekbogdanski
Copy link
Member

What is the purpose of this pull request?

New feature

Does your PR contain necessary tests?

All patches which change the editor code must include tests. You can always read more
on PR testing,
how to set the testing environment and
how to create tests
in the official CKEditor documentation.

This PR contains

  • Unit tests
  • Manual tests

What changes did you make?

Added throttling function based on CKEDITOR.tools.eventsBuffer.

Closes #1993

@mlewand
Copy link
Contributor

mlewand commented May 25, 2018

Seems that this branch got somehow broken, there's a little too much commits in there 🙂

Copy link
Contributor

@mlewand mlewand left a comment

Choose a reason for hiding this comment

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

Some things to be resolved. On top of this this code is a copy and pasta from CKEDITOR.tools.eventBuffer we should refactor both methods to use a common code base.

foo = 'foo',
buz = 'buz',
message,
buffer = CKEDITOR.tools.throttle( 200, function( arg ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Once again, use sinon.stub / spy for function mocking, it will make the API much, much clearer. 🙂

If you'd use sinon, you had assertion like that:

sinon.assert.calledOnce( inputSpy );
sinon.assert.calledWithExactly( inputSpy, 'foo' );

And then:

assert.areSame( 2, inputSpy.callCount );
sinon.assert.calledWithExactly( foo.getCall( 1 ), 'buz' )

Just to make few examples. It's a really convenient API.

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem with sinon.assert is that it doesn't allow for meaningful assert messages. because of that issue, I will use assert.isTrue( inputSpy.calledOnce, msg ) assert.

buffer.input();
buffer.reset();

wait( function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe you could even call it synchronously after reset and check the counter.

core/tools.js Outdated
* Throttles `input` events (or any `input` calls)
* and triggers `output` not more often than once per `minInterval`.
*
* After each `input` scheduled `output` will be destroyed and replaced with
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this is clear, but it could be only me.

If multiple calls occur within as single minInterval time, the closest (soonest?) interval call will use the last passed arguments and discard arguments that were given in preceeding calls.

Aaaand looking at above ☝️ it doesn't look any better 😄 I think we'll need to ask other devs to help us out here. But it's crucial this bit is understandable, as it's a very important information.

Copy link
Member Author

@jacekbogdanski jacekbogdanski May 30, 2018

Choose a reason for hiding this comment

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

TBO I'm not sure if we need information about discarding arguments in preceding calls since preceding call (there is only one previously scheduled call) is discarded. However, I think that the sentence:

If multiple calls occur within a single minInterval time, the most recent input call with its arguments will be used to schedule next output call, and the previous output call will be discarded.

best corresponds to how this function works. Although I'm not sure if it's enough understandable.

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 it works well 👍

core/tools.js Outdated
* After each `input` scheduled `output` will be destroyed and replaced with
* new one. It gives you the opportunity to modify passed parameters into `input` function and get
* different results.
*
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 it make sense to put a note that the first call is always guaranteed to be executed synchronously.

core/tools.js Outdated
*
* buffer.input( 'foo!' );
* // 'foo!' logged immediately.
* buffer.input( 'buz!' );
Copy link
Contributor

Choose a reason for hiding this comment

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

The third word in foo bar used most commonly is baz so let's stick to foo bar baz. 🙂 And you should reorder it here.

core/tools.js Outdated
* new one. It gives you the opportunity to modify passed parameters into `input` function and get
* different results.
*
* var buffer = CKEDITOR.tools.throttle( 200, function( message ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Listings should be wrapped with triple backticks + language hit 🙀

core/tools.js Outdated
* @since 4.10.0
* @param {Number} minInterval Minimum interval between `output` calls in milliseconds.
* @param {Function} output Function that will be executed as `output`.
* @param {Object} [scopeObj] The object used to scope the listener call (the `this` object).
Copy link
Contributor

Choose a reason for hiding this comment

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

Technically it's not a scope… it's a context. There is a difference between the two (please have a read on it and don't repeat the mistakes that the Ancient Ones did 🙂). So context or contextObj and a proper docs updates.

buffer.input( foo );

assert.areSame( 1, output );
assert.areSame( foo, message );
Copy link
Contributor

Choose a reason for hiding this comment

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

Good luck with figuring out as any of the assertion fails. In other words, please, put meaningful assert messages when possible and applicable.

For sure this test case is one of them.

buffer.input( foo );

assert.areSame( 2, output );
assert.areSame( buz, message );
Copy link
Contributor

Choose a reason for hiding this comment

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

No reason to check the message here, as it couldn't be changed if the call count is the same.

CHANGES.md Outdated
@@ -7,6 +7,7 @@ New Features:

* [#1761](https://github.com/ckeditor/ckeditor-dev/issues/1761): [Autolink](https://ckeditor.com/cke4/addon/autolink) plugin supports email links.
* [#1703](https://github.com/ckeditor/ckeditor-dev/issues/1703): Introduced Mentions plugin providing autocompletion feature for usernames.
* [#1993](https://github.com/ckeditor/ckeditor-dev/issues/1993): Added [`CKEDITOR.tools.throttle`](http://docs.ckeditor.com/ckeditor4/docs/#!/api/CKEDITOR_tools.html#method-throttle) function.
Copy link
Contributor

Choose a reason for hiding this comment

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

It should land in the API section.

@mlewand
Copy link
Contributor

mlewand commented May 29, 2018

I have also pushed a single test case explicitly checking that the last argument is always used for throttled functions.

assert.isTrue( inputSpy.calledWithExactly( foo ), 'Call argument the after second call' );

wait( function() {
assert.isTrue( inputSpy.calledOnce, 'Call count after the second call timeout (1st)' );
Copy link
Contributor

Choose a reason for hiding this comment

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

This assertion does not display call count as it says. Actually I'd rather see that in the assertion, so:

assert.areSame( 1, inputSpy.callCount, 'Call count after the second call timeout (1st)' );

core/tools.js Outdated
* Throttles `input` events (or any `input` calls)
* and triggers `output` not more often than once per `minInterval`.
*
* After each `input` scheduled `output` will be destroyed and replaced with
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 it works well 👍

Copy link
Contributor

@mlewand mlewand left a comment

Choose a reason for hiding this comment

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

Code looks much better, however you did not address one important point from review summary:

Some things to be resolved. On top of this this code is a copy and pasta from CKEDITOR.tools.eventBuffer we should refactor both methods to use a common code base.

We can extract it to a separate issue though.

The only thing that we need to work on are the docs. I'll adjust it in a following PR and I think we're good.

core/tools.js Outdated
@@ -579,6 +579,95 @@
}, milliseconds || 0 );
},

/**
* Throttles `input` events (or any `input` calls)
Copy link
Contributor

Choose a reason for hiding this comment

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

It has nothing to do with events, we're talking about input being a function here.

core/tools.js Outdated
* @returns {Object}
* @returns {Function} return.input Buffer's input method.
* Accepts parameters which will be directly passed into `output` function.
* @returns {Function} return.reset Resets buffered events — `output` will not be executed
Copy link
Contributor

Choose a reason for hiding this comment

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

Buffered events calls.

@mlewand
Copy link
Contributor

mlewand commented Jun 1, 2018

I have adjusted things that I was missing. Request from #1996 (review) extracted to #2045.

@mlewand mlewand merged commit 77470cc into t/1751 Jun 1, 2018
@mlewand mlewand deleted the t/1993 branch June 1, 2018 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants