Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #273 from ckeditor/t/272
Browse files Browse the repository at this point in the history
Fix: Prevent infinite loops on `.once()`. Closes #272. Closes #204.
  • Loading branch information
oskarwrobel committed Jan 31, 2019
2 parents b754f13 + 68ff7f9 commit 54b8108
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 12 deletions.
16 changes: 12 additions & 4 deletions src/emittermixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,20 @@ const EmitterMixin = {
* @inheritDoc
*/
once( event, callback, options ) {
let wasFired = false;

const onceCallback = function( event, ...args ) {
// Go off() at the first call.
event.off();
// Ensure the callback is called only once even if the callback itself leads to re-firing the event
// (which would call the callback again).
if ( !wasFired ) {
wasFired = true;

// Go off() at the first call.
event.off();

// Go with the original callback.
callback.call( this, event, ...args );
// Go with the original callback.
callback.call( this, event, ...args );
}
};

// Make a similar on() call, simply replacing the callback.
Expand Down
25 changes: 17 additions & 8 deletions tests/emittermixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,20 +324,29 @@ describe( 'EmitterMixin', () => {
sinon.assert.calledWithExactly( spy, sinon.match.instanceOf( EventInfo ), 1, 2, 3 );
} );

it( 'should be removed only after exact event fired', () => {
const spy1 = sinon.spy();
const spy2 = sinon.spy();
it( 'should be removed also when fired through namespaced event', () => {
const spy = sinon.spy();

emitter.on( 'foo', spy1 );
emitter.once( 'foo', spy2 );
emitter.once( 'foo', spy );

emitter.fire( 'foo:bar' );
emitter.fire( 'foo' );
emitter.fire( 'foo:bar' );

sinon.assert.calledOnce( spy );
} );

it( 'should be called only once and have infinite loop protection', () => {
const spy = sinon.spy();

emitter.once( 'foo', () => {
spy();

emitter.fire( 'foo' );
} );

emitter.fire( 'foo' );

sinon.assert.callCount( spy1, 4 );
sinon.assert.calledTwice( spy2 );
sinon.assert.calledOnce( spy );
} );
} );

Expand Down

0 comments on commit 54b8108

Please sign in to comment.