** Summary ** The Tcl core '''after''' command stops execution of the current thread for a fixed delay, arranges for the execution of a script some time in the future, or works with already scheduled scripts. ** Synopsis ** : '''after''' ''ms'' : '''after''' ''ms'' ''script'' ?''script script ...''? : '''[after cancel]''' ''id'' : '''[after cancel]''' ''script script script ...'' : '''[after idle]''' ?''script script script ...''? : '''[after info]''' ?''id''? ** Description ** '''after''' ''ms'': ''Ms'' must be an integer giving a time in milliseconds. The command sleeps for ms milliseconds and then returns. While the command is sleeping the application does not respond to events. The ''after'' command serves several functions: * after nn -- suspend operation for nn msec * after nn body -- register body to be executed after nn msec * after cancel id -- withdraw a registered body * ... When the first argument is numeric, it describes the length of delay in milliseconds. Otherwise it must be one of the words '''[after cancel%|%cancel]''', '''[after idle%|%idle]''' or '''[after info%|%info]'''. With a milliseconds argument but no others, the current thread (or the whole process if running single-threaded) will be suspended for at least that length of time; the exact delay is operating system dependent. With additional ''script'' arguments, the [concat]enation of those scripts is scheduled for execution at least the given number of milliseconds in the future (again, dependent on the OS); the execution of the script will be in the global scope. This second case works using timer events in the event loop, and as such will only be performed if the event loop is being serviced (e.g., via [vwait], [update] or [Tk]'s master loop). ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/after.htm%|%man page%|%]: ** See Also ** subcommands: [after cancel]: [after idle]: [after info]: Other: [bgerror]: [update]: [vwait]: [every]: [after command extended]: [bgsleep]: [idle]: an example of how to cache idle commands [Tail call optimization]: [AM] using [after] to create tail-recursive procedures ** Two Modes of [after] ** On Oct. 7, 2003, [aricb] wrote on c.l.t.: After has a "synchronous" mode and an "asynchronous" mode. The synchronous mode is in the form [[after $milliseconds]]. In this case Tcl does nothing for $milliseconds. Then it processes the next line in your script. The asynchronous mode is [[after $milliseconds $script]], where Tcl schedules $script to execute (via the event loop) after $milliseconds has passed. Tcl then returns to whatever else it was doing. In this case, after returns an id which you can use in conjunction with [[after cancel $id]] or [[after info $id]]. Here are a couple of procs to demonstrate the difference: ====== proc sync {} { after 1000 puts "message 1" puts "message 2" } proc async {} { after 1000 [list puts "message 1"] puts "message 2" } ====== ** Sleep ** This defines a command to make Tcl do nothing at all for N seconds: ====== proc sleep {N} { after [expr {int($N * 1000)}] } ====== This arranges for the command wake_up to be run in eight hours (providing the event loop is active at that time): ====== after [expr {1000 * 60 * 60 * 8}] wake_up ====== ** Repeated Action ** Repeated action is a typical application, e.g. this little timer from the [Bag of Tk algorithms]: See also, [every] '''Clock display on label: ''' ====== proc clock:set var { global $var set $var [clock format [clock seconds] -format %H:%M:%S] after 800 [list clock:set $var] } pack [label .l -textvariable myclock] clock:set myclock ;# call once, keeps ticking ;-) RS ====== This is not a recursion, the next instance of ''clock:set'' will be started long after the current has returned, and it won't go deeper in stack level. The command will be restarted every 800 msec (in this case), with a different id each time. For keeping the current id for canceling, ---- See also [An analog clock in Tk] which is powered by ''after''. ''KBK'' (15 November 2000) [Countdown program] has a better discussion of what's going on. (I feel justified in saying this, since I wrote both of them.) ** At ** Here's a '''sugaring''' for [after] where you specify absolute time, like for a scheduler: ====== proc at {time args} { if {[llength $args]==1} {set args [lindex $args 0]} set dt [expr {([clock scan $time]-[clock seconds])*1000}] after $dt $args } ;# RS at 9:31 puts Hello at 9:32 {puts "Hello again!"} ====== If you need something to schedule, this little alert packages details from tk_dialog away, and may reappear after 5 minutes: ====== proc alert {time text} { if [tk_dialog .[clock clicks] "Alert at $time" $text info 0 OK Re-Alert] { after 300000 [list alert $time $text] } } at 9:55 alert 10:00 "Meeting in 5 minutes" ====== ** after 0 ** This schedules a script for immediate execution. It's useful for getting the tightest possible event ** after x after idle ... ** [['Xplain bout how "after 0 $script" is valuable, and also safer than "after idle $script", 'cause an "after idle" body can't "after idle".]] [[[Lars H], 29 Aug 2004: Yes, someone '''please do that'''. Also explain the ====== after 0 {after idle {callSomeProc}} ====== which seems to be preferable to both of them. What is it that makes it so?]] [Philip Smolen] July 28, 2014 Is this what you mean by "after idle" body can't "after idle". https://groups.google.com/forum/#!topic/comp.lang.tcl/YqmL-MBjfLQ I'm looking at bad_idle_proc1. ** Invisible Errors ** [interp bgerror] and its predecessor, [bgerror], are scheduled for execution when an error occurs in a script queued by [after]. They essentially run with [after idle] priority, which means that they can be preempted by scripts scheduled with an [after] command that causes other tasks to run prior to the [interp bgerr] proc: ====== proc every {ms body} { after 1 [info level 0] if 1 $body } set ::j 0 after 0 { every 0 { puts "what is the length..." #normally the error notice the error in this line puts [string llength hello] incr ::j } } vwait ::j ====== The solution is to make sure scheduled [interp bgerror] actions get run: ====== proc every {ms body} { after 1 [after idle [info level 0]] if 1 $body } set ::j 0 after 0 { every 0 { puts "what is the length..." #normally the error notice the error in this line puts [string llength hello] incr ::j } } vwait ::j ====== ** How to stop the execution of a procedure from within another procedure ** ** Dependence on System Time ** [[after]] depends on the system time, so changing the system time after something has been scheduled can cause undesired behaviour. [FW]: ====== proc again {} { puts "Hello." after 1000 again } ====== ... Chaninge the system time backwards an hour in Windows as the script is running, stop receiving "hellos". I'm guessing the event loop schedules "after" events to occur at a certain fixed time, dependent on the system clock (so of course setting the time backwards will postpone scheduled "after" events), but WHY? Why not just use an internal clicker rather than the system clock? And more importantly (for my project) is there a way to avoid this behavior? notes from #Tcl irc channel, 2012-12-24 (paraphrased) [kbk]: In the #Tcl irc channel 2012-12-14, [KBK] said that the reason for this is that, as he understands it, it's quite hard to do a monotonic clock portably He also said the [ferrieux] believes that [[after]] really should be an [[at]] command to schedule a task to wake up at a given wall clock time. Windows time-since bootload promised to be monotonic, but has only (typically) 20-50 ms precision, and overflows after a few weeks. [Twylite] 2013-11-15: The dependence on system time really should be noted in the man page. Moreover the current Windows implementation trades off accuracy (against the wall clock) to gain precision (presumably for high-precision timing), and in doing so breaks both. When running under load the calibration loop seems to lag and the clock (Tcl_GetTime) drifts by up to -1.1 seconds from the system time, then jumps to catch up (this is not [http://support.microsoft.com/kb/274323%|%KB274323%|%]). That jump can result in [after] (or any userland timing based on [clock]) returning up to 1.1s early or claiming an elapsed time of up to 1.1s too much, and also means that log timestamps are up to 1.1s out compared to those of other processes (whether Tcl or other language). I'll file this as a bug in the near future. [http://www.python.org/dev/peps/pep-0418/%|%Python PEP-418%|%] is an excellent reference for implementing monotonic clocks. ** Misc ** *** Example: wait for program (executable) to become active *** This example script waits until the configured program can be seen in in the Linux /proc filesystem. I use it in a startup script (yes, tclsh is fully functional) to wait for a service. Alternative solution: master the systemd dependencies to get it right. Techniques used: timed repeated execution, advanced glob search, how to time out, how to use /proc ====== # execute this in intervals proc waitforproc {p interv} { set extrawait 500 foreach e [glob {/proc/[0-9]*/exe}] { if {[file readable $e]} { if {[file readlink $e]==$p} { after $extrawait set waitforme true return } } } after $interv waitforproc $p $interv } # called on timeout proc timeout {msg} { puts stderr $msg; exit} set progname /usr/bin/xv # start timeout after [expr {5 * 60 * 1000}] timeout "Time has run out, stopping." # look for program waitforproc $progname 200 puts "\tstart waiting for $progname" # go into the event loop vwait waitforme puts "\tstopp waiting" ====== ---- ** Dustbin ** The information in this section has been deemed inaccurate, outdated, unhelpful, or misleading, and is scheduled for deletion unless someone moves them out of this section soon. [RJM]2004-07-29: When short (< 10 ms), well defined intervals are desired, do not be tempted to use ''after nn''. Instead use ====== after ''nn'' {set _ 0}; vwait _ ;# or another variable name ====== This keeps the event loop alive. I found out that a simple ''after 1'' may yield a very different result (Win98/266MHz 4-5 ms; W2K/1200MHz 15-16 ms), while the result is reasonable accurate when the code example above is used. But from ''after 2'' on, both variations yield much too high delays (at least on the windows platform). ---- This script illustrates events at various intervals ====== proc print {} { global ary state puts "$state $ary($state)" } proc timer {} { global ary state num print after $ary($state) { set state [expr ($state+1)%$num] timer } } array set ary {0 100 1 200 2 300 3 400 4 500} set num [array size ary] set state 0 timer ====== ---- [caspian]: When you use the "after" command to make your script wait for a period of time, the rest of your script will not wait up for the line(s) that are passed through to the after command. For example, this code: ====== puts "I know" after 500 {puts "Tcl"} puts "and Tk" vwait forever ====== Will output: ======none I know and Tk # Then, 500 milliseconds later: Tcl ====== To make "and Tk" appear after "Tcl", you must make "and Tk" wait for an equal or greater amount of time as "Tcl". To wit: ====== puts "I know" after 500 {puts "Tcl"} after 500 {puts "and Tk"} vwait forever ====== which will output: ======none I know # Then, 500 milliseconds later: Tcl and Tk ======none Another way to solve this problem is by using [vwait] like this: ====== set wait 0 puts "I know" vwait wait after 500 {set wait 1} puts "and Tk" ====== [rdt] says: don't you have to do the 'after 500 ...' _before_ you do the 'tkwait ...' ?? [RJ] - Absolutely - once in the event loop, no further commands are processed, so the after never gets registered. This is a wait forever. [MG] The other option is to just use the form of [after] which pauses execution completely, instead of the form caspian used which executes one particular command after a delay: ====== puts "I know" after 500 puts "Tcl" puts "and Tk" ====== ---- [AMucha] 2008/07/28 '''after cancel script''' deletes exactly one instance! I accumulated heaps of after-procs in an overloaded text widget (trying to be super clever) with an ====== after cancel show:detail after idle {sfter 5000 show:detail} ====== show:detail uses several functions of the textwidget and (tried to) clear up with its own 'after cancel show:detail' at the end. Demo showing this: ====== proc hello {} {puts hello} for {set n 1} {$n<=4} {incr n} { after 20000 hello } foreach id [after info] { puts "$id [after info $id]" } after cancel hello puts "====================" foreach id [after info] { puts "$id [after info $id]" } exit ====== Despite the word "match" in the manpage there is no globbing. eg 'after cancel hell*' does not work. ** End of Dustbin ** <> Category Command | Tcl syntax | Arts and Crafts of Tcl-tk Programming