adding multiple event listeners

I’m curious as to how many people can answer this without running the code. What will the following print when the user taps the rect?

local button = display.newRect( display.contentCenterX, display.contentCenterY, 200, 200) for i = 1, 100 do local closure\_i = i button:addEventListener("tap", function() print ("I am tap listener " .. closure\_i) end) end

I’m also curious as to what people think should actually happen. I am torn between liking and being frustrated with what actually happens. Given the documentation does not go into detail, is this intended behaviour?

For bonus points will the following behave differently?

local button = display.newRect( display.contentCenterX, display.contentCenterY, 200, 200) local count = 0 local tapListener = function() count = count + 1 print ("I am static tap listener and have been called " .. count .. " times") end for i = 1, 100 do button:addEventListener("tap", tapListener) end

What a strange way to ask for help. :smiley:

If you add 100 tap event listeners to your button, in both cases, then I would expect the respective functions to be called 100 times. With the first, you’ll get numbers from 1 to 100 every time, but with the latter the numbers keep on growing because of count.

The only real difference that I can think of is that with the former you have a declared function that you are referencing to, which means that you can remove an event listener that calls it, whereas when you create the function within the addEventListener method, you don’t have an easy reference to it and the easiest way to get rid of the listeners is to remove the button itself.

There is nothing to stop you from adding multiple event listeners to a single object, this is intended. Now, the extra credit question with sugar on top is why would you want to add a hundred tap listeners to a single display object?

That’s great to hear that it’s intended. It’d be nice if the docs made it clear. I think that Corona should throw an error in the second case, as I can’t see a good reason why in normal use you would ever want to do it. It must always be a mistake. In the first case you might want to add or remove listeners which extend behaviour - though I think that that approach is tricky as Corona does not let you query or explicitly control the execution order of the event handlers.

I came across this issue with the following code:

 local listener listener = function() buttonImg:removeEventListener("tap", listener) func() end buttonImg:addEventListener("tap", listener) function buttonObject.reset() buttonImg:addEventListener("tap", listener) end

The bug occurred when I reset the button before the user had tapped it. In the end I fixed it by adding a boolean check, but it would have been quicker if Corona errored or printed a warning when the same listener was added again.

 

So, the problem isn’t with the listeners, but with how you’ve written the functions. This is really the same thing as with your other post about setFocus.

Think about it like this. In order for Corona to block such behaviour automatically or to let you know when you are making any kind of mistake, it would require for these function/method calls to be rewritten with a varying number of extra checks which in turn means that it would take longer for them to run.

So, most (if not all) programs would become slower to run even if 99% of developers wouldn’t require these extra precautions and even if they would only be helpful during production. Furthermore, there may be developers who do require multiple touch/tap event listeners to fire in their projects and if Corona were to block this for all developers, then they’d need to implement some workarounds.

@tap32 I don’t think Corona needs to throw and error for those events as they essentially only did what you told them to.  Unlike people, if you tell function to walk off a cliff, it will!  :slight_smile:

As for managing listeners, I find it easier to only use “touch” listeners and then fine tune their behaviors.  The “began” phase is essentially a tap and you can add a timer to measure the length of the tap if you need to know the length of the touch.

Thanks, I see your point, but I think that the performance issue is a red herring. Most apps will probably not be creating event listeners very often, so even with expensive checks I don’t think it would be an issue. You could also just run them in the simulator.

 

@tap32 I don’t think Corona needs to throw and error for those events as they essentially only did what you told them to.  Unlike people, if you tell function to walk off a cliff, it will!

Oh, I totally agree. However I think that it’s also acceptable for an API to codify its assumptions. In other languages you might raise an exception for instance. If event listeners supported some sort of interrogation, I could even write code myself which would guard against the above.

Imagine the following:

function buttonObject.reset() if buttomImg:hasEventListener("tap", listener) then return end buttomImg:addEventListener("tap", listener) end

That would be cool.  Corona is open source so if you have the time, inclination and skill set, you could add that to the SDK.  There is someone in community working on Linux builds - it was a priority for him more than the community as a whole but it adds to the whole Corona ecosystem which is good for us all?

Hmmm… after digging in the C++ code for a bit, it actually looks straightforward. It’s actually in this Lua file, which the C++ code seems to invoke. I can’t quite make sense of the control flow though: when running Corona is it calling into the C++, which is then calling the Lua code?

It could just be a case of modifying the below:

https://github.com/coronalabs/corona/blob/ccad6fd867f1745589e5b1a8e6fb3cf28a7e1a09/platform/resources/init.lua

Look at line 125

So looking at that code, you can see the issue, which causes the above: there is just an array of event listeners. If it was changed to a table, we could probably remove and check for event listeners faster, but you could only have one listener for each key (or you could have an array per key).

I don’t know though whether adding a function there would require a matching function in the C++ class.

Absolutely, the performance impact would be negligible, but I am still not a fan of adding overhead and complexity to code in places where it isn’t necessary, no matter how insignificant.