Finish Executing AppleScript Before Executing Key Commands

Maybe Chris (or anyone of course) knows this off the top of his head. Not a huge deal but I notice if I move too fast executing the macros that include it moves the cells up or down too quickly. Is there anyway to have Keyboard Maestro make sure the AppleScript is finished executing before executing key commands in a macro? QuicKeys seemed to do this very well, meaning that I can have key commands executed in a macro surrounding an AppleScript and it will wait for the AppleScript completed signal before moving on. This way I could just trigger the macro several times (i.e. Command+j, j, j, j and it will do the command four times and make sure everything is done in the right order).

I have noticed multiple times it seems that Keyboard Maestro doesn’t seem to wait for the AppleScript to finish executing and moves ahead if you repeat a macro that includes key commands mixed with AppleScripts.

I have put the key commands in the AppleScript to make sure it executes in the right order like this

> ---------
> delay 0.5

> --Up Arrow
> tell application "System Events" to key code 126 using {shift down}

> tell application "Microsoft Excel"
> 	tell active window

> 		set fontColorList to {}

> 		tell selection
> 			set valueList to its value
> 			set rowList to its rows
> 		end tell

> 		repeat with i in rowList
> 			set end of fontColorList to color of font object of i
> 		end repeat

> 		set value of selection to reverse of valueList

> 		set fontColorList to reverse of fontColorList

> 		set n to 0

> 		repeat with i in rowList
> 			set n to n + 1
> 			set color of (font object of i) to (item n of fontColorList)
> 		end repeat

> 	end tell
> end tell

> --Down arrow
> tell application "System Events" to key code 125 using {shift down}

> --Return
> tell application "System Events" to key code 36 using {option down, shift down}

> ------------

The problem with this is you have to put a delay in it and lift up your modifiers each time or you might get other commands executed that use the modifiers. I have even implemented checkModifierKeys in the past but then you have to lift off the modifier keys each time you execute the command.

----------
try
	set modifierkeysPressed to true
	repeat while modifierkeysPressed
		set modifierkeysPressed to (do shell script "/Library/Scripts/checkModifierKeys") is not "0"
		delay 0.2
	end repeat
	
on error
	display dialog "You do not have 'checkModifierKeys' installed."
end try
----------

In short is there a way to have Keyboard Maestro wait for the AppleScript to finish before starting the repeat command?

Keyboard Maestro ensures (in as much as it is possible) that an action is complete before the next action starts. So

  • Execute AppleScript
  • Type Keystroke

Would happen in sequence (of course, Type Keystroke adds a key to the event queue which may be processed at any latter date which can add some issues to this if you put them around the other way - there is no way to know when the typed keystroke will have been processed by the target application).

However if you execute the same (or different) macro multiple times, they will all run simultaneously.

If you want them to run only once, then you need to explicitly control that, which you can do trivially by adding a Semaphore Lock at the start of the macro.

Sounds like what I am after, sorry to be doofus but I am not able to get this to work. I have read what is in the manual (not much from what I could tell) and the three posts on this forum. Is there an example of a macro using this? I saw that I don't need to do the unlock and that it automatically happens when the Macro is completed. I gave the Semaphore Lock the same name as the macro, left the name the same but it doesn't seem to be working.

Update with correct file
Swap Cells Up and Down.kmmacros (60.2 KB)

Semaphore Lock
Wait and lock a semaphore to ensure exclusive access/execution.
Semaphore Unlock
Unlock a semaphore (happens automatically if macro terminates).

Where is the AppleScript?

Your macro is simply: Semaphore Lock, Key1, Key2, Key3, Key4, Key5. What that will ensure is that even if this macro is executed twice simultaneously, those keys will be added to the queue in that sequence twice. You will always get:

Key1, Key2, Key3, Key4, Key5, Key1, Key2, Key3, Key4, Key5

You will not get something like:

Key1, Key2, Key3, Key1, Key2, Key4, Key3, Key5, Key4, Key5

But it does not show the execution of the AppleScript, so that will happen independently.

You need (according to the original post), something like:

Semaphore Lock, Execute AppleScript, Key1, Key2, Key3, Key4, Key5.

1 Like

Thanks Peter and sorry about that, I uploaded another macro that didn’t’ have the AppleScript included (to which the Semaphore Lock also helped. I uploaded the macro with the AppleScript in the post above.

Just so I am a bit more clear, the name seems to not matter all that much from my testing. Is the only reason you give it a name if you want to use the unlock in the middle of a macro. No examples come to mind when I would need the unlock, I was thinking maybe you would use it with a second Macro you run on a infinite repeat loop or something but once the Macro completes the first time it unlocks it to run in sequence next time it is run.

Also it seems like I would want the Semaphore lock on every macro I ever use, especially if it is the same macro run a second time. I am confused at how commands can butt in line if they are executed later and there is no delay.

Also just guessing here but maybe it is because from what I noticed in another post is that there is a default delay built into every key command that Keyboard Maestro runs. Maybe there is a preference that could be changed that all macro’s need to be complete before the next one runs. I personally at this point don’t have macros that would run for hours, minutes, or seconds that can’t complete before the next one is run.

Had to learn why it is called Semaphore, never would have found that out on my own https://en.wikipedia.org/wiki/Semaphore_(programming), it kind of seemed like it should be called Macro lock but I guess you can have multiple key commands in-between AppleScripts in a single macro? Sorry so many questions maybe there is some reading you can point me to with more examples of using Semaphore’s in Keyboard Maestro macros.

The name is so you can have multiple independent locks. Eg, you might be using the lock you describe, and you might also want a backup script to run every hour, but if the backup took more than an hour, you would not want two backups to run at once, so you could use a lock to avoid that, and use a different name so as not to stop your other macro from waiting for the backup to finish before executing.

It would certainly have been a valid design decision for every macro to run in sequence, but that has never been how Keyboard Maestro operates. Keyboard Maestro can have many different macros all executing "simultaneously" (they actually don't execute simultaneously, its more like tiny pieces of each macro execute in succession).

If every command executes immediately, then the sequences would be in lock step. So imagine a case where two macros are executed in relatively quick succession, but each macro consists solely of 10 Set Variable to Text actions (which never have to wait for anything). The sequence might be something like:

A1, A2, A3, A4, B1, A5, B2, A6, B3, A7, B4, A8, B5, A9, B6, A10, B7, B8, B9, B10

But many actions have to wait for something (roughly 40% of all actions do not complete immediately). Even actions like typing a key wait a period of time, and that time may not be precisely consistent. Once that happens, the second macro may catch up or pass the first macro depending on the various timings of its actions.

It would be a perfectly valid design decision. It's just not how Keyboard Maestro works, and I would not want to add a preference that completely changes how macros are executed.

Sure, but 9 times out of 10 when you go and add that macro you would long since have forgotten you toggled some obscure preference and wonder why all your macros stop working.

I don't know of any particularly good article on this. Hopefully one day the wiki page on the action will include lots of good information, but it does not even exist yet.

For the same reason that there is no such preference, and that you would be happy to enable the preference, it really is an action that is rarely needed - most macros are independent of all other macros, and most macros run very quickly, and rarely do you want to do anything else while doing them, so the action is not often required and thus not well known or documented.

Thank you Peter very much for all the excellent answers and clarification and thought why things are done the way they are. It is very helpful to know your thought and that I am not missing something and adding Semaphore locks is the way to go.

That makes a lot of sense, especially in the light that there is a default pause you built in for each key command repeat and action.

If the command key is down, the delay is stored in the SimulateKeystrokeCommandKeyDelay preference (default 0.15)
defaults write com.stairways.keyboardmaestro.engine SimulateKeystrokeCommandKeyDelay -float 0.15

You can set the delay to wait after simulating a keystroke before continuing to the next action (default 0.0)
defaults write com.stairways.keyboardmaestro.engine SimulateKeystrokeDelay -float 0.01

If it is not too much crazy trouble could you make two macros that link to each other with one that has the Semaphore lock where it should be and the other with the Semaphore lock for the end. Something like in the example you gave where a backup shouldn't start again until the other one is finished. I think it would greatly help me wrap my head around the use of especially the end part. It seems like there might be cases where you might want a couple Semaphore locks in one macro but still not quite certain (just dense, I am sorry).

That is great you have a wiki place holder for the subject, a temp link to this thread might help some poor soul.

Thanks for your skill and wizardry!

The normal case is that macros just all run simultaneously, and you rarely have multiple macros running at the same time.

Most macros are executed explicitly by the user, and the user expects them to do their thing and then the user goes on to the next thing, so macros like this will rarely run at the same time since the user is highly aware of what is going on. In a few cases like this you want to be able to run a macro multiple times and have it execute the entire macro in sequence - this is a primary purpose of using a Semaphore Lock - just place one at the start of the macro.

Indeed, you could have one of these with name "UI" at the start of pretty much every user interface macro you use and that would probably even be a good thing. Its also mostly pointless, but would occasionally do the right thing when you triggered two macros quickly for example.

But then there is another class of macros that operate in the background, most especially time triggered macros. Macros that are triggered independent of the UI and the user's awareness. Typically these will be written so as not to impact the user since they wont be expecting them to run. So they might be things like backup scripts, or automatic download scripts, or the like. Things that happen in the background, or that wait for the user to be idle before doing stuff in the foreground. Often these sorts of macros run for a significant amount of time.

If there was a Semaphore Lock on every macro (or if Keyboard Maestro ran only one macro at a time), these macros would stop all other macros from operating.

In some alternate dimension, Keyboard Maestro could be designed so that all macros defaults to behaving as if they had a Semaphore Lock at the front of them, and those few macros that should run independently were somehow marked otherwise. And that would be a perfectly valid design decision - its just not how Keyboard Maestro does it. And it would have its own downside when users didn't know to allow the macro to run independently and thus have their backup macros stop all their other macros from working (or worse, have them all queued up and run after the backup finished which would be a disaster in the making).

As for an example case where a lock is needed for only part of a macro, that is unusual in Keyboard Maestro since there are no concept of local variables, so even changing the variables a macro is working on will potentially mess up the execution of the later part of the macro. Otherwise, an example that might be the case would be a backup followed by post processing the backup folder, something like this:

  • Semaphore Lock "Backup"
  • Set Variable to "Backup Directory" unique name for new backup folder
  • Backup to "Backup Directory"
  • Semaphore Unlock "Backup"
  • Post Process "Backup Directory"

But since the variable "Backup Directory" would get smashed by the next instance of the macro, that would fail. But that is the sort of case where you would ordinarily unlock part the way through an action. Without local variables though, this does not really work, and I'm not sure of another example that would work where you want to unlock part of the way through.

1 Like

That makes sense, I can see the logic here, I never ran back-ups with QuicKeys so I wonder how it handled these sort of things. Things as a user going on behind the scenes that I just didn't think about. Thank you for the Macro example and disclaimer you gave. Sounds like I probably will never need to use a Semaphore unlock from what I gather from your posts.

Thanks Peter you should add all that to your Wiki page as you answer these questions so well in the forums!

Yes, I should, and the wiki is slowly being built up with this sort of thing.

Nice to see this here, had to revisit this again so I thought I would just post an update for anyone coming across this thread.
https://wiki.keyboardmaestro.com/action/Semaphore_Lock?s[]=semaphore&s[]=lock

1 Like

4 posts were split to a new topic: How Do I use Sub-Macros & Prevent One from Starting Until the Other has Finished