Macros vs Subroutines

Subroutines are new with Version 10.

Now we have two ways to execute a macro or subroutine. Subroutines can both be launched with either "Execute Macro" or "Execute Subroutine", but Macros can only be launched with "Execute Macro"

If we use the "Execute Macro" then we can choose to run the macro or subroutine asynchronously but if it's a subroutine then we lose the ability to define a return variable.

If we use the "Execute Subroutine" then we can't choose to run a macro, and the "Execute Subroutine" operation does not allow subroutines to run asynchronously.

Is the plan to allow "Execute Subroutine" to run asynchronously?

1 Like

Let me summarize my thought:

The macro action can run asynchronous, and parameter can be passed . But if multiple parameters are required, it need to implicitly passed as json string or delimited string.

On the other hand, probably subroutine is designed to have return value, and can pass multiple explicit parameters, and may not make sense to have return value from asynchronous mode.

Subroutine can have a return value action by the way through [Return From Subroutine] Action

as long as subroutine does not have return value, asynchronous mode make sense. Wonder if Peter can add that mode.

I didn't add any macro attachment since it just a illustration.

*do define parameter with Local_ private access because otherwise they become global variables.
Local_myName, Local_myAge

2 Likes

Even if a subroutine has a returned value, asynchronous mode makes sense. For example imagine wanting to OCR multiple small rectangles of the screen and return a text value of each location. Doing this in serial would take longer.

Unless the subroutine return value is irrelevant. What happen is that subroutine caller will not able to get the return value correctly in aync mode because 'async' subroutine will return immediately while still processing in background.. So most likely you are referring to assign the value in Instance or global variable in subroutine.

I agree. And I would add that even it is to return a value, we may still put a Wait Until Subroutine return a value in the main macro. @peternlewis

I don't know if I'll add Async execution for subroutines. Maybe.

This sounds overly complex and confusing.

Hi Martin,
that's correct, once subroutine can have async mode support, other action(s) need to support that, then it start to make sense. Async support in subroutine action by itself, without support from other action will not make sense if caller depend on the return value. Add async in subroutine probably complicate KM.

If Peter can add async support in subroutine and other relevant action (eg wait check), it will be another great feature.

Peter,
yes, unfortunately it does add complexity to KM.

To use Execute a Macro as an example:

  • Run the main macro
  • The main macro runs async macro using Execute a Macro, which saves some data to a variable (let's give it a name instance__var1)
  • The main macro continues to run, but it pauses at some point, until instance__var1 is not empty.
  • The main macro then continues

In the above example, users can save some time by running the other macro async.

What I said earlier is just applying this process to Subroutine Macro. But handling return from subroutine does sound more difficult to deal with than pause until variable is not empty. Probably this makes it overly complex.

That said, I don't have a real use of it now. This is just a conceivable usage that might benefit some users. I'm completely fine with not having it. After all, we may always use Execute a Macro instead.

Just stumbled across this thread but it's helpful because I was just wondering why execute a macro has the option to run asynchronously while the execute subroutine action does not.

I have a handful of subroutines that I like to have run asynchronously since the calling macro does not need any kind of reply from them. So in those cases it seems to make more sense to embed them as a "execute a macro" action rather than a "execute a subroutine" action that way they can continue to be run asynchronously.

I've pondered your question. I think the answer is because there are some subroutines that return values, and it isn't even physically possible for those subroutines to return their value if they are running asynchronously because the macro that called them could have already terminated when the asynchronous subroutines terminates.

But, in theory your idea could work when subroutines are never returning a value. But there are questions you need to answer for the situation where a subroutine tries to return a value and the calling macro has ended. What do you want KM to do in that case? An answer to this question must exist. And since you are proposing this idea, you should answer it.

In fact, even if the macro that does the calling hasn't ended, I'm not sure it's safe to modify a value that that macro may be busy using. That could open up a whole new class of possible error conditions.

But I'm just a noob who tries to answer tough questions.

EDIT: I think you somewhat raised these questions in your second paragraph, but you didn't give an answer.

Hmmm... I don't think I did. Have a subroutine running asynchronously and finishing after the calling macro hadn't even really occurred to me to be honest. I definitely don't have any macros built that way (that I know of haha).

Just to shed more light on my earlier comments though, one of the most common subroutines I use is to pause/resume my media players. the pause subroutine runs an AppleScript to determine what is playing (Music, Spotify, TV, VLC, QuickTime), pauses it and sets that to a variable. But the calling macro doesn’t actually need to know what was playing so I am able to run it asynchronously without any issues. The resume subroutine works much the same way but resumes the media player.

That's a realistic and good example to talk about. But I don't quite see why you need an asynchronous subroutine to do that instead of a macro with the asynchronous flag on. You aren't passing the name of the media player as a parameter, for example.

P.S. I sure hope you are using semaphores to prevent simultaneous execution of the same macro. This is a perfect example if when semaphores are useful.

I don't run those as via the "Execute a subroutine" action, I run them via an "Execute a macro" action. I call them subroutines because that's the naming convention I started using for those kinds of macros a long time ago (and most of them are in a macro group called Subroutines haha), long before KM10 and the "Execute a subroutine" action existed. So sometimes I say a subroutine, but I don't refer to a subroutine action, but rather a macro that functions as a subroutine. Hopefully that clarifies things. :+1:t2:

Oh yea, I make heavy use of Semaphore Locks!

Hi, any chance you can post a copy of the pause subroutine? I would find that useful. I often hit my Play/Pause key forgetting that I'm listening to something other than the Music app.

Hey I'd be happy to. I do need to know what media players you use however, because I'll have to modify the AppleScript to include only the ones you use otherwise it will throw an error.

-Chris

Cool! But if you're short of time I could give a go at hacking away at your script. I'm on Monterey and use Music, Infuse, VLC, TV, Spotify. I imagine it would be difficult to figure out if a video is playing in specific Safari apps and pause that, and am sure it would be hard to start videos in Safari as well. I'm thinking YouTube, Netflix, Crave, Disney+, that kind of thing.

No problem at all! Since it's a little off topic I'll send you a DM!

I am looking for article about the difference between subroutine and execute a macro. This article talks a lot about async and sync. since I'm not a programmer, I don't know much about this. but luckily I almost never enable async in my kmm.

At the beginning, I don't use subroutines until I see the convenient of passing variable value in and out of subroutines (even only can pass one var out). I start a big project.

The macros can be called by other macros in my KM all has a prefix in macro name - handler. this word is from AppleScript. I'm not sure if I use this word in correct way, but I really have many hundreds of handler macro in my KM.

I begin doing this handler-sub-conversion. Convert a handler to sub – then use a macro find on this forum – find macros that use selected sub-macro – to find all callers and then go into each of them to change execute a macro to execute sub.

So my question is:

If I don't use async, and don't use input parameter & return from subroutine, and use instance__ and global variables to pass variable value in & out,

Execute a macro
&
Execute subroutine

Are they completely all the same to each other?

If the subroutine need to pass many variables in & out, I choose to keep using the old method - instance__ & global var, but I just find that did this to some macros will lead to some error. The macro doesn't work like before.

I want to post this macro here. but it's too complex and involve a lot of find image on screen. I think it's not easy to replicate the issue on my system. so I ask about this in theory firstly.

Make an example.
here is an simple macro to plus two var value.

And now I convert it to a sub:

But note that, I still use Instance__ to pass var In & Out.
Now In Macro A I call the handler;

And in Macro B I call the sub:

NOTE that in A & B,
all the same except

Handler becomes subroutine
Execute a macro becomes execute sub

So My question: can I have same result in both macros?

For these two macros, YES.
But does this works to all the cases all the time??

call the sub and call the macro - all they same.kmmacros (13.3 KB)