Web Development with Visual FoxPro
WWWC and async tasks, a basic performance study
Gravatar is a globally recognized avatar based on your email address. WWWC and async tasks, a basic performance study
  Mike Helland
  All
  Sep 18, 2025 @ 10:54am

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.

Gravatar is a globally recognized avatar based on your email address. re: WWWC and async tasks, a basic performance study
  Rick Strahl
  Mike Helland
  Sep 18, 2025 @ 06:42pm

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 ---

Gravatar is a globally recognized avatar based on your email address. re: WWWC and async tasks, a basic performance study
  Rick Strahl
  Mike Helland
  Sep 18, 2025 @ 06:45pm

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 ---

Gravatar is a globally recognized avatar based on your email address. re: WWWC and async tasks, a basic performance study
  Mike Helland
  Rick Strahl
  Sep 19, 2025 @ 05:12am

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.

Gravatar is a globally recognized avatar based on your email address. re: WWWC and async tasks, a basic performance study
  Rick Strahl
  Mike Helland
  Sep 19, 2025 @ 11:10am

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 ---

© 1996-2025