TIP: Simple Try/Finally Example

Simple Try/Finally Example

What is "try/finally"?

The "try/finally" programming paradigm is a way to "try" to do some actions, with some "finally" actions that you can guarantee will always happen, even if the actions in the "try" section fail, or exit, or whatever. Usually we're talking about having some sort of "cleanup" actions in the "finally".

Here's the example we'll be using here:

image

We show the progress window in line #1, and we want to make sure it gets closed no matter what.

So with the use of try/finally, line #9 will always happen, regardless of whether the lines 3-7 succeed, fail, or anything else.

When would I need to use a "try/finally"?

Anytime you have "cleanup" actions that need to get performed, no matter how the macro ends.

Keyboard Maestro

Keyboard Maestro doesn't have a native "try/finally" action (yet), but we can roll one ourselves, and it's surprisingly simple and effective.

TRY

Here's a simple "try" macro, using our example logic from above. The "green" actions are the ones you would replace:

image

  1. Lock a semaphore using our UUID (unique identifier) as the name.
  2. Display the progress bar. Or whatever you need to protect in a "finally" section.
  3. Execute our "Finally" macro asynchronously, passing the name of the semaphore (which in this case is our UUID).
  4. Do whatever we want our macro to do.

Semaphore names just need to be unique. We're using our macro's UUID because we know it's unique. We could have named it "Ralph" and it would still work, as long as another macro didn't want to use a semaphore named "Ralph" for a different purpose.

Asynchronous, from the wiki:

You can optionally configure the action to be Asynchronous, which means the sub-macro will be executed as a new execution instance, and the current macro will continue on without waiting so that both macros will be executing at the same time.

FINALLY

The "Finally" macro does cleanup work, which in this case means closing the progress bar.

Here's our simple "Finally" macro:

image

  1. This is the key action. It tries to lock the semaphore, using the name passed in our trigger value. But the semaphore is already locked by the "try" macro. So the "finally" macro waits until the semaphore is available. The semaphore will become available when the "try" macro ends - no matter how it ends.
  2. Once the "try" macro ends, the "finally" macro will close the progress bar.

Having a macro wait on a semaphore uses practically no CPU resources. It is extremely efficient.

Using the Examples:

Just replace the green actions with whatever you want.

Source Macros:

There's two sets of "try" and "finally" macros in this file. One set doesn't have any comments, to make it simpler to follow. The other set has lots of comments, so if you have questions, check out those comments first.

Try Finally Example Macros.v1.0.kmmacros.zip (7.2 KB)

4 Likes

Can I sheepishly ask what the difference is between doing this and just setting the action that might fail not to abort the macro if it does?

No sheepishness needed!

It's certainly not needed in every macro you write, that's for sure. Especially in the case of the progress bar, which you can close manually.

But if you have to do "cleanup" actions (like closing the progress bar) in more than one place, then this helps eliminate that.

Consider places where you may conditionally cancel the macro, or "return" from a subroutine. You might need to do cleanup there too.

So I would say this: If you find yourself doing the "cleanup" action in more than one place, then you might want to consider a try/finally.

4 Likes