22.2. Apple EventsApple events are expensive, and some Apple events are very expensive. You can't do anything about the time spent waiting for each Apple event to execute, but perhaps you can minimize the number of Apple events sent. At the same time, you may be able to improve the efficiency of the particular Apple events you do send. The boolean expression at the top of a repeat while block must be evaluated before every repetition of the block, and then once more in order to decide not to repeat the block any further. This means that it should not contain any commands whose result will not change during the repetition, as it would be needless and wasteful overhead to issue those commands each time through the loop. Here's a silly but telling example. Suppose we have two folders in the Finder, and we want to create enough new folders inside the first folder so that it contains the same number of items as the second folder. The following code expresses neatly and elegantly what we want done: set x to 1 tell application "Finder" set f1 to folder "f1" set f2 to folder "f2" repeat while ((count items of f1) < (count items of f2)) make new folder at f1 with properties {name:("f" & x)} set x to x + 1 end repeat end tell But in the world of AppleScript, neat and elegant isn't always good. That code sends the count message to the Finder twice for each time through the loop, when in fact we need only send it twice at the outset as we prepare for the loop: set x to 1 tell application "Finder" set f1 to folder "f1" set f2 to folder "f2" set c1 to count items of f1 set c2 to count items of f2 repeat while c1 < c2 make new folder at f1 with properties {name:("f" & x)} set x to x + 1 set c1 to c1 + 1 end repeat end tell Observe that the same issue would not arise if I had coded this using repeat with x from...to, because that construct evaluates everything once at the outset and then never again. Special considerations arise when using repeat with...in (see Chapter 19). The Apple events you send may be needlessly numerous and needlessly complex. Suppose we want to ask BBEdit to make uppercase every word starting with "t": tell application "BBEdit" repeat with w in every word of document 1 if contents of text of w begins with "t" then change case w making raise case end if end repeat end tell That looks very neat, but looks are deceptive. Each time through the loop, w is set to this: a reference to item 1 of every word of document 1 of application "BBEdit" a reference to item 2 of every word of document 1 of application "BBEdit" a reference to item 3 of every word of document 1 of application "BBEdit" And so on. Our code is sending BBEdit an Apple event for every word in the document, and this Apple event asks BBdit to evaluate the concept every word afresh each time, even though the meaning of that concept is not changing significantly as we loop. If there are a thousand words and just two beginning with "t", that's a massive waste. We can eliminate the repeated evaluation of every word by gathering the words of the document once, before the loop: tell application "BBEdit" set L to (get every word of document 1) repeat with w in L if contents of text of w begins with "t" then change case w making raise case end if end repeat end tell But we are still sending one Apple event for every word in the document. With a single much more efficient Apple event at the outset, we can ask BBEdit to give us a list of just those words we will need to change: tell application "BBEdit" set L to (get every word of document 1 where contents of text of it begins with "t") repeat with w in L change case w making raise case end repeat end tell That way, even if our document consists of a thousand words, if there are just two words beginning with "t", our code will send just three Apple events: one to gather the list of references to the two words, and then two more to change their case. Be alert for implicit Apple events you may be sending without intending to do so. Reread Chapter 12 to see how capturing a reference implies the possibility that using that reference might send an Apple event. In a loop, that sort of thing can add up. Finally, consider that you might not have to loop at all. The target application may be smart enough do what you want with a single command. There's no need for the loop in this code to gather the names of Finder folders: tell application "Finder" set L to (get every folder) set L2 to {} repeat with f in L set end of L2 to name of f end repeat end tell Instead, you can say this (see "Operations on Multiple References" in Chapter 11): tell application "Finder" set L to (get name of every folder) end tell |