Waiting
wait pauses the handler for a time, in seconds, milliseconds or ticks (sixtieths of a second):
put the milliseconds into tStart
wait 250 milliseconds
put the milliseconds - tStart into tTook
put tTook >= 240 and tTook < 1000
true
While a plain wait lasts, the whole program waits with it: a click is not answered, and a timer does not fire. That is fine for a moment's pause, but not for anything longer. wait ... with messages lets the rest of the program carry on during the wait, and wait until waits for a condition to become true:
local sDone
on finish
put true into sDone
put "finished"
end finish
put false into sDone
send "finish" to me in 300 milliseconds
wait until sDone with messages
put "waited for it"
finished waited for it
Later: send in
Usually you do not want to wait at all: you want something to happen later while the program goes on. send (Messages and events) with in and a time does exactly that. It sets a timer and returns at once; when the time comes, the message arrives like any other:
on ring pTimes
put "Ring!" && pTimes && "times"
end ring
send "ring 3" to me in 300 milliseconds
put "The timer is set."
The timer is set. Ring! 3 times
The message is text, so the values for its parameters go after its name, as you would type them: here 3. To pass a value from a variable, join it in: send "ring" && tCount to me in 1 second.
Again and again
A handler that sets a timer for itself runs again and again: this is how a clock ticks, a game moves, or a program checks for new mail. Give it a way to stop, or it goes on until the stack closes:
local sCount
on countUp
add 1 to sCount
put "count" && sCount
if sCount < 5 then
send "countUp" to me in 100 milliseconds
end if
end countUp
put 0 into sCount
countUp
count 1 count 2 count 3 count 4 count 5
Unlike a repeat loop, a handler that sends itself lets everything else happen between its runs: clicks are answered, fields can be typed in, and other timers fire. It is the right shape for anything that goes on over time.
Timers still to come: cancel
send ... in leaves the timer's id in the result. the pendingMessages lists every timer still to fire, a line each (its id, when it fires, the message and where it goes), and cancel stops one by its id:
on alarm
put "alarm!"
end alarm
on nudge
put "the nudge arrived"
end nudge
send "alarm" to me in 5 seconds
put the result into tAlarmID
send "nudge" to me in 200 milliseconds
put the pendingMessages into tPending
filter tPending with "*,alarm,*"
put the number of lines of tPending
cancel tAlarmID
put the pendingMessages into tPending
filter tPending with "*,alarm,*"
put the number of lines of tPending
1 0 the nudge arrived
The IDE has timers of its own, which is why the example filters the list to its own message. A program that stops (a game that ends, a window that closes) should cancel its timers, so none fires into something that is no longer there:
on closeCard
repeat for each line tLine in the pendingMessages
if item 3 of tLine is "countUp" then
cancel item 1 of tLine
end if
end repeat
end closeCard
Measuring time
the milliseconds counts thousandths of a second, and the seconds whole seconds, both from a fixed moment long ago. One reading means little; the difference between two is a length of time:
put the milliseconds into tStart
repeat 100000 times
add 1 to tCount
end repeat
put "That took" && the milliseconds - tStart && "ms"
That took 21 ms
The number depends on the computer, so the example's Output is only one run's. Making code fast uses this to compare two ways of doing the same thing.
Try it yourself
Write a countdown: a handler countDown that puts its number, then sends itself the number one less, 200 milliseconds later, and puts Go! when it reaches 0. Start it from 3.
Show a solution
on countDown pFrom
if pFrom > 0 then
put pFrom
send "countDown" && pFrom - 1 to me in 200 milliseconds
else
put "Go!"
end if
end countDown
countDown 3
3 2 1 Go!
Words in this lesson
Each one finds every lesson that uses it. The Dictionary has the whole story of each, with its syntax and more examples.