Ive been in here bitching about this language every day, so I figured I’d change it up and instead just talk about an interesting aspect of it.
The Idle handler… does anyone use it for anything interesting?
The last time I used it was back in the 90s when I was first getting in to scripting. I had this one script running 24-7 on my old Performa . . . but sadly I cannot remember what it actually did. Like, at all. I had this script always running doing something for me back then but its function has fully left my brain. And I don’t have any of my old personal data from 97 to 03 so it will forever be a mystery.
But this post isn’t about my old scripts. I’m sure there are plenty of interesting scripts that make good use of the idle handler… if you have one, tell us about it!
The on idle handler is less about having interesting use cases and more a tool for good coding practice. It is available to employ for stay-open applications, which are intended to run continuously in the background. The handler is the only safe and efficient means AppleScript provides for repeatedly executing code on a loop.
The code inside the on idle handler is executed, after which the handler exits with a numerical return value optionally supplied by the coder (defaulting to 30, I think) that defines the number of seconds until the handler is to be called again. Provided nothing else is executing elsewhere in the script, these seconds are genuinely spent idling for the interval period, and it does so very efficiently with next to zero consumption of resources.
This is the key difference from the common novice approach:
repeat
(* do stuff *)
delay 1
if condition then exit repeat
(* do stuff *)
end repeat
Back when all code was executed on the one and only available thread, any hint of a potentially infinite code loop was a real danger to system integrity. Nowadays, of course, there’s a lot more scope for debating the extent to which constructs like the above remain a relevant issue when discussing best coding practices.
It is somehow still possible for AppleScript to be caught inside an execution loop ftom which it cannot break out, but it’s not a common scenario and I’ve not heard of it compromising the system in any significant manner. That said, because repeat loops like the above keep a script actively running for the entire duration of the loop, it can absolutely have a noticeable impact on system resources. There’s no idling within the loop, and even the delay command doesn’t suspend execution; rather it remains an active process. If an AppleScript is expected to spend a long time inside a repeat loop, it will eventually eat up memory and force a reboot.
I haven’t used idle in a looong time, but do use NSTimer on occasion (the idle handler is essentially called by a one-shot timer that gets reset to the result). I think NSTimer is a little more flexible, for example it can be shut off, and in situations where a UI object such as a statusItem or window is used, it can also be used in a script editor since the editor keeps a reference (at least until it is quit or the script releases it).
Both are definitely better than using a repeat loop with a delay, which also tends to jam up the UI.
Would NSTimer be able to run loops inside Script Debugger?
I’ve been meaning to ask if there’s a way to run an idle handler multiple times in SD, but never got around to it. If idle can’t do its actual job — idling and running, idling and running — there’s a limit to what you can debug in the editor.
The script I used it for has been replaced with a Swift daemon that’s fully event based, which is much lighter on CPU usage. (I was looking for an excuse to learn Swift and Xcode, but I figure it would be doable with ASObjC too.) Some things may still call for polling, though.
NSTimer can be used in a script editor, but since scripts don’t stay open, something needs to keep a reference so that the script instance is retained. Since various objects are actually added to the script editor (the current application), a timer can be added to the script editor’s runLoop, which will keep it running until it is invalidated (or the script editor is quit).
use framework "Foundation"
use scripting additions
property timer : missing value
property updateInterval : 2 -- time between updates (seconds)
property timeLimit : 5
on run -- example
set my timer to current application's NSTimer's timerWithTimeInterval:updateInterval target:me selector:"update:" userInfo:(missing value) repeats:true
current application's NSRunLoop's mainRunLoop's addTimer:timer forMode:(current application's NSDefaultRunLoopMode)
end run
to update:sender -- called by the repeating timer
say timeLimit
set my timeLimit to timeLimit - 1
if timeLimit < 0 then sender's invalidate() -- stop
end update:
Depending on what the timer is doing, a countdown limit should probably be used when testing so that so that the timer eventually stops, otherwise you may wind up with a bunch of zombie script instances.