removeEventListener weird behavior on html5

I have a class ProjectileEnemy.hx like this :

and create 4 bullet like this :
ss1

In the beginning, all 4 bullet moving fine (from right to left). But after I called destory() on bullet A and bullet B, bullet C skipped function update() 1 frame and caused the position bullet C left behind bullet D like this :
ss2

If I not call removeEventListener(Event.ENTER_FRAME, update) - line 46. Bullet C moving fine, not skipped function update() 1 frame.

This case only on html5, I tried on flash everything working fine. Not test on android yet.

Does it help to replace openfl.events.EventDispatcher with an older version?

For example: https://raw.githubusercontent.com/openfl/openfl/ea86318b2cf0acf4ab7edfd3a7c7f4fbb195ceda/openfl/events/EventDispatcher.hx

Nope, still have same issue.

I would like to be able to help fix this issue, but I’m not sure that I have a way to reproduce what you are seeing.

In the meantime, you might want to consider using one ENTER_FRAME listener, which then calls update on all of your projectiles that are active?

Yes, my solution at this moment did that.

Sounds like the code iterates forwards. I didn’t look at the actual source, but I assume it’s something like this:

var i:Int = 0;
while(i < listeners.length) {
    var listener:Event -> Void = listeners[i];
    listener(event);
    i++;
}

(This construction is equivalent to for(var i = 0; i < listeners.length; i++) in other languages.)

This normally works fine, but if you remove a listener in the middle of that loop, all of the listeners after that will be shifted backwards in the array. For instance, if you remove listener B (at index 1), then listener C will be placed at index 1. However, the iterator variable i, which was 1, will increase to 2 by the next iteration. But now index 2 refers to listener D, not C. So C is just skipped.

On the next frame, when i is set back to 0, there isn’t any issue. That’s why you only see one frame of difference.

A common solution to this problem is to iterate backwards. When a listener removes itself, only the following listeners are updated. So when listener B (index 1) removes itself, indices 1-3 are modified. However, since you’re iterating backwards, the next value of i is 0, and listener A is still in index 0.

Now, the problem with iterating backwards is that it isn’t efficient on all devices. Some devices optimize assuming you’re going to look up memory addresses in ascending order, so if you iterate backwards, it causes several cache misses in a row. This is less of a problem in modern computers (which may be able to handle both), but OpenFL is meant to run on so many devices, it’s almost guaranteed that at least one has this issue.

Other than iterating backwards, OpenFL could check each iteration for removed listeners (impractical), use a linked list (inefficient), or request that you don’t remove ENTER_FRAME listeners while they’re running (inconvenient).

Actually, one more option I thought of would be to track distance from the end of the list:

var i:Int = -listeners.length;
while(i < 0) {
    var listener:Event -> Void = listeners[listeners.length + i];
    listener(event);
    i++;
}

This is a strange way of iterating, but it will access values in order, and if you remove one partway through, then the iterator will move back. However, if you add a listener partway through, it’ll jump forwards and skip something. And if you remove something other than the current listener, it would cause even more problems (but that applies to every solution I’ve mentioned).