Help with timer.cancel() again (SOLVED)

Hey everyone!

My timer cancel returns false. It seems like I totally miss that handle syntax(

local clearPhase

function showWindow
 	local cl = timer.cancel(clearPhrase)
 	print(cl) --> returns false
 	...
 	timer.delay(5, false, function(self, clearPhrase)
 		hideWindow()

 	end) end

Maybe some example in timer API reference might answer a lot of similar questions.

1 Like

This is wrong. timer.cancel() takes a timer handle (unique id). A call to timer.create() will return a handle/id for the created timer:

local handle = timer.create(2, true, cb)
timer.cancel(handle)

The same in the callback function:

timer.create(2, true, function(self, handle, time_elapsed)
    timer.cancel(handle)
end
2 Likes

Look at the example and follow values in the variables:

local clearPhase = 99  --> clearPhase is 99

print("clearPhase outside", clearPhase) --> "clearPhase outside 99"
local cl = timer.cancel(clearPhase) --> 99 is not a number representing any created timer handle at this point
print("return", cl)                     --> returns false as in your example

timer.delay(1, false, function(self, clearPhase) --> use name "clearPhase" as reference to value passed to this callback by timer.delay that is invoking it!
	print("clearPhase inside 1:", clearPhase) --> "clearPhase inside 1: 0" - 0 is a number representing handle to the timer created here
-- ! Important notice here - by passing the name "clearPhase" here you won't get that variable changed to the handle of the timer, instead a new local reference is created here that holds actual handle value for the scope of the function only!
end)

timer.delay(2, false, function(self) --> do not cover the name "clearPhase" here
	print("clearPhase inside 2:", clearPhase) --> "clearPhase inside 2: 99" because you are referencing now to the outer local variable called clearPhase, which was 99
end)

In the first timer you are telling the timer:

  • “Hey! Get my callback signature and put your handle in the second argument I called clearPhase”
  • “Ok, but you know you defined clearPhase earlier (as 99), right? :smirk: But your wish is my command, I am creating a variable here with timer’s handle value (0)
    If you will use clearPhase here now, you will get timer’s local handle (which is 0), not that outer local (99)”

In the second timer:

  • “Hey! Print me the value of clearPhase”
  • “Hmm, I don’t see clearPhase in the scope of this function, let me take a look outside… :thinking:
    O! There it is, clearPhase is 99!”

If you are familiar with C++ you might use a knowledge about a difference between passing by value and passing by reference :wink:

Now on to your case, as you have a better understanding now:

At this point you are either passing a nil variable here, as you only declared it above with local clearPhase (you can print it before and note that in the snippet you provided is a typo in Phrase) or you defined it somewhere above, but it is not in the snippet here. In the first case, nil is not a proper timer id to cancel and you should get an error like this:

ERROR:SCRIPT: /main/handler.script:10: bad argument #1 to 'cancel' (number expected, got nil)

If you didn’t get this error you must have defined somewhere above clearPhrase as some number, which is still not a proper timer handle and thus you have a false returned.

Next, you try to somehow “use” clearPhrase to “pass” it to the function “by reference” (or like a pointer to which you would like to save the handle’s value), which is a callback function invoked, when 5 seconds elapses in your timer.delay call - that’s how I see it and that’s probably how you understand it, right?

As you may suspect, this is not a proper understanding, but don’t worry - look: :wink:
The third argument of timer.create is a callback function:

When “delay” amount of seconds elapses the callback function is called AND parameters are passed into it - self (object), handle (id) and time_elapsed value.
When you are writing its signature like this:

you can then use in the body of this function self as a reference to self object and clearPhrase as handle. That clearPhase here is a different variable than what is declared above as local.

I hope this helps you understand the case! :slight_smile:

2 Likes

Seems like I see why it won’t work like I did) But it didn’t help me to stop the timer that is already running from outside of it’s callback.

I’ll try to give more details. I have a dialog function (showPhrase) that shows character saying “I won’t do that here”. It has a timer.delay at the end of a function that commands to hide dialog in 5 seconds. But if player clicks the same button soon after that, that timer.delay is still running and hides dialog panel with wrong delay. That’s why I need to cancel that timer.delay right the beginning of showPhrase. To let new timer start new delay of a new phrase.

Seems like now I know how to cancel it inside that delay, but I need to cancel it outside.

If I understand it right, what you need to do before setting your timer is check if you already have one running. If you do, cancel it, then store the reference to your new timer.

I’m trying, but still there’s something I’m missing. Besides being dumb)

local clearPhrase = nil

local function showPhrase()
   if clearPhrase then
	   timer.cancel(clearPhrase)
   end
   ...
   timer.delay(5, false, function(self, clearPhrase)
	   clearPhrase = 99
	   clearDialogs()
   end)
end

That doesn’t look correct

it should probably be:

local clearPhrase = nil
1 Like

I’d do something like this.

local clearPhrase = nil

local function hide_dialogue_after_delay()

	--cancel any existing timer
	if clearPhrase then
		timer.cancel(clearPhrase)
	end

	--start new timer
	clearPhrase = timer.delay(5, false, function()

		clearDialogs()

		clearPhrase = nil
	end)

end
4 Likes

But it almost what I did right at the start!) Works now. Thanks everyone!

OMG I was so close)

1 Like