Another Stab at getting a Try/Finally action

@peternlewis I've asked for this before, but I'd like to ask for it again - a Try/Finally action.

Here's a typical scenario, which I'll explain below:

image

I'm doing a loop, displaying a progress bar from 99 down to 1.

#1 is where I display the current progress.

The remaining numbers 2-4 are 3 places where I have to make sure I close the progress window.

#2 - If the Escape key is pressed, I exit the macro, so I need to close the progress bar.

#3 - If an error occurs, I need to close the progress bar.

#3 - When the loop is done, I need to close the progress bar (because I'm counting down, instead of up).

Even if I were counting up, and ending at 100, I'd still have to close it if there's an error.

Here's what a try/finally would look like:

image

I only have to close the progress window in one place, because the "finally" actions are always guaranteed to happen.

For instance:

  1. When the macro is exited with "Cancel This Macro", the "finally" part is executed before program flow exits the macro, so the progress window closes.

  2. If an error occurs, the "finally" actions are executed before the error is "raised" (e.g. program flow ends and an error message may or may not be displayed), so the progress window closes.

  3. If the "while" loop exits normally, the the progress window closes.

This is extremely common in various programming languages. A typical scenario is when you open a file, and you want to guarantee it gets closed no matter what else happens.

Or, as in this case, you display a progress window, and you want to make sure it gets closed.

Here's a JavaScript explanation - even though it says "catch", it discusses "finally" also:

2 Likes

I would love to see this, too. The way I end up doing this is by setting a flag variable for each condition that needs to trigger whatever that "gonna have to do this in a few places" action is. Then I just have the action once, in a Case statement that checks the flag variable.

But "Finally" would be oh so much cleaner.

-rob.

Your request is good, but I have a different request below that would help me solve this problem in a different way.

First, I will mention two ways that I would solve this problem. The first option isn't quite what you want, but is very simple, and might help in other situations. The second option is exactly what you want, but a little complex.

First, you could simply replace "Cancel This macro" with the action "Throw". If you did that, the catch block would always be executed. The only side effect is that it wouldn't actually Cancel. It appears what you want is a "cancel but don't quite cancel yet" functionality.

Second, you could use a regular Try/Catch action, but place the following in the Catch action (keeping in mind that you still need to use the Throw action in the Try block.) If you did the following, I think you would get the exact functionality you want. But it's a few extra steps.

image

As an alternative proposal for Peter, I would propose a new action that would simplify the following:

image

... into some new action like this: (pardon my sloppy editing)

image

What this action would do is set the value of MyResult to either 1 or 0, depending upon the list of conditions below it. I would use this action a lot, as it would reduce many triple actions down to a single action. I see absolutely no reason why KM couldn't or shouldn't implement this action. I have always needed it, and it would even help a bit with the Try/Finally situation.

It's late, and I've run out of available time reconstructing your macro from the image, so I haven't tested this, but could you not get what you need by splitting part of the macro off into a subroutine and returning the result (including one for when escape is used)?

If you are talking to me, that would sometimes work, but in the example I gave it would not work, because "Last Action Result" is cleared if you call a subroutine. I can give other examples of conditions that would also fail in a subroutine (certain tokens, certain variables.) And even if it did work, I would rarely create a subroutine for a three-action statement. The lack of a statement that sets a variable to 0 or 1 based on a condition seems like an oversight to me.

So if I understand this, you want a different new action, Try/Finally, that behaves:

  • Execute the actions in the Try section.
  • If an abort happens, execute the actions in the Finally section, and assuming they run to completion, re-throw the Abort.
  • If an abort does not happen, execute the actions in the Finally section, and then continue without an error.

And there is no Catch section.

Is that correct?

Not quite.

The finally section is sort-of like "make sure you close the door behind you".

In the following explanation, when it talks about actions that fail, things probably happen differently depending on if the failure is supposed to abort the macro, and whether the user should get notified. But I hope this is a good start:

Successful Execution

try
  1: do something
  2: do another thing
  3: do one more thing
finally
  4: always do this
end-try-finally
5: do more things

All 5 actions execute as if there was no try/finally

Action Fails

try
  1: do something
  2: do another thing that fails
  3: do one more thing
finally
  4: always do this
end-try-finally
5: do more things

Actions 1 & 2 execute, then 4. #5 does not execute and the user is notified of the error. Note that the notification occurs after the "finally" code runs.

Cancel Macro

try
  1: do something
  2: if some condition, cancel macro
  3: do one more thing
finally
  4: always do this
end-try-finally
5: do more things

Actions 1 & 2 execute, and if "some condition" is met in #2 and "cancel macro" happens, then #4 executes, then the macro is canceled. #5 does not execute.

Successful Execution, but Error in Finally

try
  1: do something
  2: do something else
  3: do one more thing
finally
  4: always do this, but it causes an error
end-try-finally
5: do more things

Actions 1-3 execute, but #4 causes an error. #5 does not execute, and the user is notified of the error.

Action Fails, but so does an action in Finally

try
  1: do something
  2: do something else that fails
  3: do one more thing
finally
  4: always do this, but it causes an error
end-try-finally
5: do more things

Actions 1 & 2 execute, then 4 but it causes an error, so the user is notified of the error from #4. The error from #2 gets swallowed. #5 does not execute.

Finally has a Cancel Macro

try
  1: do something
  2: do something else
finally
  3: always do this
  4: If some condition, cancel macro
end-try-finally
5: do more things

Actions 1 & 2 execute, then 3, then if "some condition" is met in #4, the macro is canceled and #5 does not execute.

Can you not do this by always, deliberately, throwing an error at the end of your try block and putting your finally action(s) in the catch? So

try
  1: do something
  2: do another thing that fails
  3: do one more thing
  4: throw error
catch
  4: do whatever, depending on error from above
  5: always do this
  6: report and/or cancel, depending on error status
end-try-catch
7: do more things

I don't see where your examples differ from what I said.

Cancel is just a different form of abort (without notification).

Which of your examples do you think behaves differently to what I described?

1 Like

No, to OP.

Thanks for your reply though. I'll reread the whole thread later today.

Looks like I misread your original comment. What you said is right. Sorry for the misunderstanding.

Sometimes my ADD causes me to misread words. :roll_eyes:

1 Like