-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
makes event listener callbacks execute asynchronously #902
Conversation
dd3cb41
to
d356030
Compare
d356030
to
b725613
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! I'm surprised you fix all the tests that were failing. I expected a lot more to fail.
For future reference:
|
@jespirit Thanks for the review! i updated with feedback. take a glance and let me know if it all looks good. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upon further review, I think we should revert the changes to how the callbacks are called and instead change the unit tests so they are executed asynchronously when callbacks are expected to be called.
test/specs/EventsSpec.js
Outdated
expect(spy).not.toHaveBeenCalled(); | ||
done(); | ||
}); | ||
}, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Syntax error: }, 0);
added argument 0
to it()
instead of window.setTimeout()
}); | ||
|
||
it("'slideStop' event is triggered properly and can be binded to", function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$.trigger() is not async
I have removed the done
argument and this test should fail because the 'slideStop' callback should be executed asynchronously. However, this test will still pass because _trigger()
will call $.trigger()
synchronously, and .trigger()
will call custom events synchronously, with the exception of built-in events, which are executed sync/async depending on browser.
bootstrap-slider/src/js/bootstrap-slider.js
Lines 1876 to 1883 in a5fc012
_triggerJQueryEvent: function(evt, val) { | |
var eventData = { | |
type: evt, | |
value: val | |
}; | |
this.$element.trigger(eventData); | |
this.$sliderElem.trigger(eventData); | |
}, |
See this thread: https://stackoverflow.com/questions/14017013/is-the-jquery-trigger-function-guaranteed-to-be-synchronous
it("'slideStop' event is triggered properly and can be binded to", function() {
touch.initEvent("touchstop");
testSlider.on('slideStop', function() { // this will be called synchronously
expect(true).toBeTruthy();
});
testSlider.data('slider')._mouseup(mouse);
window.setTimeout(function() {
expect(spy).toHaveBeenCalled();
// done();
}, 0);
});
Tests Should Be Async
Upon further review,
I think the issue is not that the callbacks are being executed synchronously, I think the way we had it before was fine, it's just that the tests themselves were not being executed asynchronously.
So I think the tests should be reorganized to be as follows:
// Register callbacks
testSlider.on(/* slideStart, slide, slideStop */, function() { /* code here */});
setTimeout(function() {
// set up code here to be executed asynchronously (next tick of the event loop)
}, 0);
For example,
it("Should trigger change on mouseup", function(done) {
var changes = 0;
// Register callbacks
testSlider.on('slideStop', function(){
expect(changes).toBe(2);
expect(testSlider.getValue()).toEqual([2, 5]);
done();
});
testSlider.on('change', function(){
changes++;
});
// Execute this code async (on next tick of the event loop)
setTimeout(function() {
testSlider.mousedown(mouseEvent('mousedown', tickOffsets[1]));
expect(testSlider.getValue()).toEqual([1, 5]);
testSlider.mouseup(mouseEvent('mouseup', tickOffsets[2]));
}, 0);
});
If you remove the call to done()
then the test will timeout.
Drag handles over each other and use keyboard to move handles over each other
√ should drag and keydown handles properly to the right then back to the left
√ should drag and keydown handles properly to the left then back to the right
√ Should snap to a tick within tick bounds when using the mouse navigation
× Should trigger change on mouseup
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL. (1)
src/js/bootstrap-slider.js
Outdated
for(let i = 0; i < callbackFnArray.length; i++) { | ||
let callbackFn = callbackFnArray[i]; | ||
window.setTimeout(() => { | ||
callbackFn(val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my review comment below, the callbacks should be executed synchronously at the moment they're triggered, which is how $.trigger()
behaves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for pointing this out!
I reflected on this a bit more and I agree with you. Also, when a user triggers a mouse or keyboard based sliding event, this automatically triggers the necessary callbacks in an async fashion via the browser's native event handling mechanism.
If a user directly changes the value via the setValue()
call, then the event handlers should execute in a sync fashion (as you indicated).
I looked at another popular slider library called noUiSlider, and this is how they do it as well.
…ion from callbacks
@jespirit just removed the changes + removed the default timeout amount of 0 in the unit tests ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like every time I look at this pull request, there's something I missed. I left my review comments, take a look.
@jespirit feel free to make the changes you want if you don't want me to block you. i probably will not have time to work on this |
Background
After diving through our source code and tests, I realized a serious issue...none of our event listeners were executing asynchronously!
Though we were exposing a callback based API, the callbacks were being executed synchronously whenever the private method
_trigger()
was being executed.I wrapped the callback invocations in
setTimeout()
with a timeout of 0 (which cues the callback to be invoked on the next event loop tick) and fixed a bunch of our tests to reflect this.Pull Requests
Please accompany all pull requests with the following (where appropriate):
grunt test
in your Terminal within the bootstrap-slider repository directory