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

Commit 54b8108

Browse files
authored
Merge pull request #273 from ckeditor/t/272
Fix: Prevent infinite loops on `.once()`. Closes #272. Closes #204.
2 parents b754f13 + 68ff7f9 commit 54b8108

File tree

2 files changed

+29
-12
lines changed

2 files changed

+29
-12
lines changed

src/emittermixin.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,20 @@ const EmitterMixin = {
3232
* @inheritDoc
3333
*/
3434
once( event, callback, options ) {
35+
let wasFired = false;
36+
3537
const onceCallback = function( event, ...args ) {
36-
// Go off() at the first call.
37-
event.off();
38+
// Ensure the callback is called only once even if the callback itself leads to re-firing the event
39+
// (which would call the callback again).
40+
if ( !wasFired ) {
41+
wasFired = true;
42+
43+
// Go off() at the first call.
44+
event.off();
3845

39-
// Go with the original callback.
40-
callback.call( this, event, ...args );
46+
// Go with the original callback.
47+
callback.call( this, event, ...args );
48+
}
4149
};
4250

4351
// Make a similar on() call, simply replacing the callback.

tests/emittermixin.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -324,20 +324,29 @@ describe( 'EmitterMixin', () => {
324324
sinon.assert.calledWithExactly( spy, sinon.match.instanceOf( EventInfo ), 1, 2, 3 );
325325
} );
326326

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

331-
emitter.on( 'foo', spy1 );
332-
emitter.once( 'foo', spy2 );
330+
emitter.once( 'foo', spy );
333331

334332
emitter.fire( 'foo:bar' );
335333
emitter.fire( 'foo' );
336-
emitter.fire( 'foo:bar' );
334+
335+
sinon.assert.calledOnce( spy );
336+
} );
337+
338+
it( 'should be called only once and have infinite loop protection', () => {
339+
const spy = sinon.spy();
340+
341+
emitter.once( 'foo', () => {
342+
spy();
343+
344+
emitter.fire( 'foo' );
345+
} );
346+
337347
emitter.fire( 'foo' );
338348

339-
sinon.assert.callCount( spy1, 4 );
340-
sinon.assert.calledTwice( spy2 );
349+
sinon.assert.calledOnce( spy );
341350
} );
342351
} );
343352

0 commit comments

Comments
 (0)