0
votes

In the script included below I try to run a chain of commands. But when run by pressing F1 the command will run both SendAndLoopFor() invocations immediately after each other without waiting for the inner loop to complete.

I tried to 'force' this await by adding a return "" inside the function, and assigning a variable with it instead Foo := SendAndLoopFor() but even then it would not await the operation.

Is there a way to await the completion of the SendAndLoopFor() command before executing the next?

#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%  
#MaxThreadsPerHotkey 13
#SingleInstance
F1::
Done = 0
Toggle := !Toggle
Loop
{
    If (!Toggle)
        Break
    SendAndLoopFor("4", -13600)
    SendAndLoopFor("5", -13600)
}
return

SendAndLoopFor(TSend="", Timeout=0)
{
    Send %TSend%
    SetTimer, LoopLimit, %Timeout%
    Loop
    {
        If(!Toggle)
            Break
        If(done == 1)
        {
            done = 0
            Break
        }
        MouseClick, left
        sleep 83
    }
}
LoopLimit:
    Done = 1
Return
1
You need a return before SendAndLoopFor(). And you refer to a non-existent label named LoopLimit - Jim U
Ah i indeed forgot to include that piece. Ill edit it in later - MX D
AutoHotkey does run them in sequence, but your SendAndLoopFor loop has a Sleep time of only 83 milliseconds. Is this intentional? Because your current delay will be almost imperceptible. - David Metcalfe
@DavidMetcalfe but the loop will go on, till interupted or for the timeout duration.which in the sample is 13.6 seconds. Thats what the settimer part is for - MX D

1 Answers

0
votes

Your problem seems to stem from a misunderstanding of SetTimer. It's an excellent tool for parallelizing tasks, because the script inherently does not wait for the timer before proceeding down the script.

To illustrate this, simplify the script and you can observe the same effect.

F3::
SetTimer, LoopLimit, -2000
SendInput, This is stuff happening before the timer.{Enter}
return

LoopLimit:
SendInput, This is stuff happening after.

A simple solution is counting iterations and killing the loop after hitting the counter limit.

F3::
count = 0

Loop
{
; Do stuff.

count += 1
Sleep, 1000

if (count >= 5)
{
break
}}

You can also use various built-in variables to compare times from the start of the loop to your cut off value, but these can be a pain to work with, so I recommend avoiding them unless your script is working within time-sensitive constraints.

Edit per the comments:

F1::
Done = 0
Toggle := !Toggle
Loop
{
    If (!Toggle)
        Break
    SendAndLoopFor("4", -13600)
    SendAndLoopFor("5", -13600)
    Sleep, 2000
}
return

SendAndLoopFor(TSend="", Timeout=0)
{
    Send %TSend%
    SetTimer, LoopLimit, %Timeout%
    Loop
    {
        If(!Toggle)
        {
            SendInput, This is inside the Toggle.{Enter}
            Break
        }
        If(done == 1)
        {
            SendInput, This is inside the Done.{Enter}
            done = 0
            Break
        }
        SendInput, This is after the If statements.{Enter}
        MouseClick, left
        Sleep, 999999 ; Note how this has no effect due to above break.
    }
}
LoopLimit:
    Done = 1