How can I return values from a listener/callback function

Hi there!

I’m currently using a function that requires a listener. Within the listener, I want to return 2 values in order to keep going on with my code. I think I’ve pinpointed my issue but I have no idea on how to resolve it.

Here are 2 exemples. The 1st one is working as expected, the 2nd doesn’t.

-- Exemple A:
local function returnFunctionA()
	local returnValue = true
	return returnValue
end

-- returnFunctionA() is immediately called
local returnValue = returnFunctionA()

-- returnFunctionA() returns a value that can be instantely checked
if returnValue == true then

	-- Keep doing stuff
	print("Keep doing stuff")
end



-- Exemple B: 
local function returnFunctionB()
	local returnValue = true
	return returnValue
end

-- returnFunctionB() is called after 10ms
local returnValueB = timer.performWithDelay(10, returnFunctionB)

-- returnFunctionB() can't return a value
if returnValueB == true then

	-- Keep doing stuff
	print("Should keep doing stuff, but doesn't work")
	
end

So, from what I can understand: once the timers is triggered, the callback function is properly called (returnFunctionB()). But then, it returns a value (returnValue). Since this function was called as a callback function, I have no way to declare the two variables that should receive those returned value.

Any clue on how I could do this?

I could use some “global” variables instead of using returned values, and use a timer to regularely check them but I don’t think it’s the cleanest approach I could use.

Also, I’ve heard about coroutones but I have never tried using them. Would it be helpful in my case?

Any clue?

First of all, when you call timer.performWithDelay() the program doesn’t stop running. You need to take that into account. You are just telling the program to run that function 10ms after that call is executed.

Second, timer.performWithDelay returns the timer object. So, it can only be used with other timer functions. More: https://docs.coronalabs.com/api/library/timer/performWithDelay.html

From what I can tell, you can do this:

-- Exemple B: 
local function returnFunctionB()
	local returnValue = true
	return returnValue
end

-- returnFunctionB() is called after 10ms
local timerB = timer.performWithDelay(10, function () 
    local returnValueB = returnFunctionB() 

    if returnValueB == true then

	-- Keep doing stuff
	print("Should keep doing stuff, but doesn't work")
    end
end, 1)

Thanks for the suggestion!

In the meantime, I’ve already tried what you’re suggesting to do. That indeed works.

Unfortunatley, your solution suggest that I keep writing my code within the anonymous function. In my project, I’m expecting the callback function to return a value so that an other function (that called that timer in the 1st place) could keep going on.

Using your exemple, I woud like to be able to do something like this:

(edited in order to make the code cleaner)

-- Exemple: 
local function returnFunction()
	local value = true
	return value
end



-- returnFunctionB() is called after 10ms
local function triggerTimer()

	local myTimer = timer.performWithDelay(10, function () 

	    local returnValue = returnFunction() 

	    if returnValue == true then

			-- Keep doing stuff
			print("Should returnValue to mainFunction()")

			-- Return values (should be retrieved by mainFunction())
			return returnValue

	    end


	end, 1)

end



-- My main function
local function mainFunction()
	
	local retrievedValue = triggerTimer()

	if retrievedValue == true then
		print("EVERYTHING'S FINE NOW!")
	end

end

mainFunction()

What I would like is to be able to return the value “returnValueB” so that mainFunction() can keep going on. Since this value is returned from an anonymous function, it seems that it returns nothing (can’t use it).

If you can tell me what you’re trying to achieve, I may be able to come up with a better solution. Are you trying to start a timer and when certain values are reached like 10th second you do something?

Ouch… I’ve accidentely removed my previous reply :frowning:

The given exemple is a simplification of what I’m trying to achieve.

In my project, I’ve got a button that, once pressed, shows an Android File Picker screen where the user is able to select a picture and import it in my project. I’m using this Android File Picker plugin.

So, instead of having a timer.performWithDelay function, I’m using this:

androidFilePicker.show("image/*", pathToExport, myListener)

This is bascially the same idea: once the button is pressed, a listener is started (myListener) that should return a value at some point. Everything works fine so far, except I’m unable to retrieve the value declared by myListener.

What I’m trying to do is: how can I retrieve return value from anonymous callback function/listeners?

Hope it helps.

I’ve edited my previous reply with an updated code.

I’m not familiar with how the plugin works but looking at Scott’s example, I assume the call would pause the application. Taking that into account, you can declare a variable right before your listener function, assign appropriate value to that if file is picked and check that when you need to.

Just one thing, when you say “instead of timer.performWithDelay”, do you mean that the program keeps running in the background? If so, you may need more security measures to prevent that. Can you make sure of it?

Yes, I can totally do that: declare a variable before calling Scott’s plugin, update that variable as soon as the user has selected a picture and use this variable to keep going on.

But due to how big my project is, I can’t really afford doing that: I’m trying to use as less variables as possible because of that.

I’m not really familiar with coding vocabulary, but I guess the way I’m doing it is usually linked to asynchronous functions. So yes: the code is still running in the background. The reason I’m using the “timer.performWithDelay” in my exemple is because it emulates the same structure as Scott’s plugin (the callback function returns a value after an undetermined time -in my case, 10ms, but in reality, it depends on the user’s action).

You may rely on async functions but looking at Scott’s example, he seems to be relying on an instant answer. My first suggestion would be to test this on a device.

Apart from that, I remember there’s a 60 variable limit per screen for forward referencing. It starts giving errors after passing the limit. Do you use that many variables in a single screen? If not, I suggest the method above. If you do, I can suggest assigning that value to an object that you already use like a player object. For example: player.isFileSelected = false

I’ve already been able to run Scott’s plugin on my device. The thing is, my problem is really only related to how my project code has been designed.

I’m now looking into other solutions (i’m currently trying to use a custom listener and I’ll look into coroutines later) as it seems it’s just impossible to transmit returned values from callback functions.

Thank you for reminding me the 60 variable limit per screen: I totally forgot about that. In my case, I don’t have any issue though: I’m already using tables to store variables. I’m just trying to avoid using variables for something like that as much as possible for better readibility.

1 Like

@evanspro — Perhaps I don’t fully understand what you are trying to do, but based upon what I understand, I would do this:

— the key here is to set these variables outside of the functions, so they can all set and test them

local receivedStatus, receivedValue	

-- My main function
local function mainFunction()

	local function callbackListener(returnedValue)
		receivedStatus=1
		receivedValue=returnedValue
	end
	
	callMyPlugin(callbackListener)

	local function waitForIt()
		if NOT receivedStatus then
			timer.performWithDelay(10, waitForIt)
		else
			print("EVERYTHING'S FINE NOW and here’s the VALUE:"..tostring(receivedValue))  —tastring is used in the event the receivedValue = nil
		end
	end

	waitForIt()

end

mainFunction()

One more thing; I wouldn’t worry about any limits to using variables, unless you are reusing the same ones. These days, both Android and iOS devices can handle tons of variables; even if your entire app used all global variables; the CPUs have no trouble with tons of _G.globalvariablenames - that said, use local when you can as I’ve done above.

@evanspro - I just figured out how to get the code formatting right above - so if you checked it when I first posted it, it wasn’t right. Now it is.