Can Applescript be multithreaded?
I am writing an Applescript for Spotify that is supposed to do two things:
- Log every new song played.
- Every N seconds check what sound device is used.
I do (1) by a simple loop that check how much of the track remains and then use that value in a delay statement in the loop (unnecessary to run the loop at full speed). However, N usually is much lower than the remaining time of the current track. Hence, I would like task (2) to run in a separate loop with N-0.5 as delay value. Basically I want to loops to run simultaneously from the same script. Is this possible?
(Of course I could use two separate scripts but I need to distribute this solution when it is done, therefore less visual complexity - i.e., two scripts running simultaneously taking up space in Dock, needing to be launched etc - is better.)
Not easily. The concept of processes and threads isn't contained in AppleScript.
You'll want another tool for that job. Swift might be a good one unless you're looking for python or something a bit more established.
Language Limits
As of macOS 10.13, AppleScript does not trivially support multithreading within a single script. It appears possible to use NSThread
within an AppleScript but this is complex and leads to a second limitation, that of the application being interacted with.
Application Limits
In addition to the AppleScript language itself not natively representing threads, the called application is unlikely to deal with AppleEvents in a thread optimal manner. AppleEvents are queued by macOS in a First-In First-Out (FIFO) order for processing by the application.
The application can defer responses to long running AppleEvents but this requires additional engineering that few applications implement. Most applications will block until the AppleEvent is processed before looking at the next AppleEvent.
This means a multi-threaded AppleScript talking to single application would likely not see the performance gains expected for the effort.
Single Event Loop?
You could approach the problem with a single loop dealing with multiple events. The single loop could sleep for N seconds, wake and deal with the pending appropriate tasks.
Piyomaru's answer shows one way to implement an event based approach. This approach is not multi-threaded or parallel; it is an event driven loop.
AppleScript now supports multi-threading (macOS 10.10 or later). And originally it has timer interrupt function since 1993 (on idle
timer event handler works concurrently within single AppleScript).
I wrote some AppleScripts with iTunes like you wrote. AppleScript can make delegates and receive other application's event.
This sample AppleScript can receive iTunes's playing state change. Once, the script run on Script Editor, it receive the change event of iTunes playing and displays name of track using macOS notification dialog.
-- Created 2017-10-13 by Takaaki Naganoya
-- 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
property NSDistributedNotificationCenter : a reference to current application's NSDistributedNotificationCenter
on run
NSDistributedNotificationCenter's defaultCenter()'s addObserver:me selector:"statusChanged:" |name|:"com.apple.iTunes.playerInfo" object:(missing value)
end run
on statusChanged:sender
tell application id "com.apple.iTunes"
try
tell current track
set anAlbum to album
set aName to name
set anArtist to album artist
set aRating to rating
end tell
on error
return
end try
end tell
display notification aName
end statusChanged:
So, the point is Spotify application can provide such a function like iTunes or not.