-
Notifications
You must be signed in to change notification settings - Fork 8
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
timer.setTimeout can call listeners that have been removed #248
Comments
const callback = dt => {
elapsed += dt;
// Convert seconds to ms and see if item has timed out
if ( elapsed * 1000 >= timeout ) {
// make sure that the listener hasn't already been removed since we may be calling this callback
// from the TinyEmitter activeListenersStack, see https://github.com/phetsims/axon/issues/248
if ( this.hasListener( callback ) ) {
listener();
this.removeListener( callback );
}
}
};
this.addListener( callback ); |
TinyEmitter's In case it helps, here is a patch that assigns a name to the listeners, and sets a debugger for the case where the listener is removed twice:
|
@jonathanolson and @samreid could you please summarize what the reasons were for calling all listeners present at the start of an emit? I found the question posed in #72 but no documentation about reasoning for the decision. |
@jonathanolson described examples in scenery where this behavior was important. My memory is fuzzy--would be great for @jonathanolson to elaborate. |
DOM events and other event systems I've worked with do typically have the convention that the array of listeners at the point of the event are the ones that will be notified. I recall some more practical examples of how it's beneficial (and avoids infinite loops if you remove and re-add a listener in a callback), but I'd have to think more on it. |
OK thanks, it sounds like this is a conventional design choice and one we want to keep for most listeners. But I don't think this is desirable for timer in particular. The example in #248 (comment) is trying to reset the elapsed time before calling a particular listener in a timeout by removing it and adding it back. And in this case that isn't possible because it was added to the @samreid are you OK if we go forward with #248 (comment)? |
#248 (comment) looks like an acceptable short term solution (especially if it's blocking progress) and possibly a good long term solution. But I'd like to understand #248 (comment) more, perhaps in a separate dedicated issue. Thoughts? |
…listener during emit(), see #248
Alright, thanks, I added the |
In an unrelated issue, I encountered a case where a listener added through
timer.setTimeout
tried to call and then remove a listener that had already been removed. The issue happens when a listener is removed from timer in a separate listener that is also attached to timer. For example, running the following in the command line will result in an error "Assertion failed: tried to removeListener on something that wasn't a listener"Because timer will try to remove
timeoutReference
again.I think this is happening because of how TinyEmitter guards against removal of listeners with activeListenersStack.
@samreid could we add a guard in setTimeout to make sure timer still has the listener before trying to call and remove it?
The text was updated successfully, but these errors were encountered: