We use WWWC and integrate with various payment processors. Performance is always important, but getting shorter request times is pretty important when there are moving parts, and web hooks, and everything should be completed in a snappy way.
One of our slow downs happens to be emails. There are async ways of sending messages, but even then, sometimes we have to authenticate first, but usually we definitely want to know if our email sent or not.
Using the built in wwAsyncWebRequest, the code uses VFP's built in RUN to call an .exe of your design.
Test1 In my tests, running this .exe (a simple VFP app) takes about 0.3 seconds. So the web request will be at least 0.3 seconds long. Not bad.
Test2 Replace RUN with shell execute. It seems to work. Even in a COM object. Now calling the exe takes about 0.07 seconds.
Test3 I like Javascript's setTimeout(), and I've wanted one in VFP for all kinds of reasons (desktop and web) so I made one. So I put the shellexecute in a setTimeout()
setTimeout([ShellExecute(0,"open","c:\manager8\awencode.exe","","",1)], 1)
Now requests effectively 0% of the request is spent running the external exe.
Here's setTimeout.prg.
lparameters tcCommand, tnTimeout, tvParam1
local lcTimerName, loTimer
lcTimerName = sys(2015)
loTimer = createobject("settimer")
loTimer.cCommand = tcCommand
loTimer.vParam1 = tvParam1
loTimer.cTimerName = lcTimerName
loTimer.Interval = tnTimeout
loTimer.Enabled = .t.
public &lcTimerName
store loTimer to (lcTimerName)
define class settimer as Timer
cTimername = ""
cCommand = ""
vParam1 = .f.
Enabled = .f.
Interval = 0
function Timer
if not this.Enabled
return
endif
this.Enabled = .f.
local lvParam1, lcCommand, lcTimerName
lvParam1 = this.vParam1
lcCommand = this.cCommand
lcTimerName = this.cTimerName
&lcCommand
if not empty(lcTimerName)
release &lcTimerName
endif
endfunc
enddefine
So far this seems to be working like I hoped. Maybe someone knows of a potential downside to doing this.

I don't see anything wrong with that.
In fact there's a wwEvalTimer
class in wwEval.prg
that does basically what you built here. 😄
I've used that in desktop applications to get around weird ActiveX timing and focus issues, but it should also work on the server.
Just realized that's not documented, but it's there...
+++ Rick ---
On second thought if all you're doing is running an EXE, you can use RUN /N
to run the exe without waiting for completion. Default behavior waits /N
runs in the background.
There should be no need to delay execution.
+++ Rick ---
I was using /N. I tested it like this:
lnstart = seconds()
lncount = 0
do while seconds() - lnStart < 1
run /n myexe.exe
lnCount = lnCount + 1
enddo
?seconds()-lnstart
?lnCount
In 1 second, only 3 loops are performed. So RUN /N is still taking 1/3rd of a second. It's blocking the client from getting their response by 350 ms.
Switching to shellexecute(), the app is launched 16 times in 1 second, blocking for 60 ms.
By using setTimeout(), I'm only "delaying" execution by 1 millisecond, which basically allows the request to complete and the stack to wind down, and the client to get their response, then the exe runs when you get back to READ EVENTS. So the call doesn't block the request for any time at all.

Hmmm... I've never looked into RUN
performance, but I'm surprised that RUN /n
would be slow and take a bit of time. I suspect the perf hit on all of these comes for waiting for acknowledgement that the process was created before moving on to the next line of code.
In the timer example that happens in the background so it still takes that time but after your other code has run. If you have a busy server, you may still end up slowing things down as VFP will time slice the timer of the main thread. I imagine if you're that worried about perf for an external process executing, you're actually going to hit that scenario!
The other thing that you can do to avoid that scenario, is to use wwDotnetBridge and the Async features to ShellExecute from there.
You can call Process.Start()
? loBridge.InvokeStaticMethodAsync(null,"System.Diagnostics.Process", "Start", "https://websurge.west-wind.com")
Note this works only on full framework. In .NET Core the StartInfo structure has to be used.
+++ Rick ---