by Dr. Peter Wayne
Another Alpha-phile sent me an email with this puzzle:
I have a page that features a number of alpha dialogue components (set up as buttons) that run a number of database maintenance routines. I would like to present the user with some feeling of progress even if it is for several seconds. Many of the solutions I have seen for a progress bar - JavaScript and the like - seem to be difficult to set up with the alpha dialogues. I would settle for displaying an animated graphic that would disappear when the page returns.
The solution to this puzzle was a little more difficult than I thought, but ultimately not too hard. And the best news is you don't have to learn any JavaScript or anything more than Xbasic and the Web App Server to implement it!
Step 1: Failure
My first approach seemed straightforward enough: use response.redirect() to send the user back to the WAS server for an updated page showing the progress of the task. Here is a simple rendition of the first approach as an .a5w page:
<%a5
if eval_valid("counter")=.f. then
counter="1"
end if
if val(counter)<10 then
? "<html><head><title>Progress</title></head>"
? "<body><P>Now we are at "+counter+"</P></body></html>"
sleep(1)
counter=alltrim(str(val(counter)+1))
response.redirect(request.script_name+"?counter="+counter)
else
? "<html><head><title>Done</title></head>"
? "<body><P>Now we are done at "+counter+"</P></body></html>"
end if
%>Script 1. “Failure.a5w”
The logic of this page – save it as “failure.a5w” if you like, publish it, and then run it – is that every second the server tells the browser to redirect itself to failure.a5w. If you look at the code, it should work, but all that happens is the browser sees the final page. That's because the WAS buffers its output and sends it all at once, so the user's browser sees the final response.redirect(“failure.a5w?counter=10), and never gets to see the earlier pages that were generated.
Step 2. Try again.
But that doesn't mean we have to give up. What we need to do is tell the browser to initiate a refresh from its side. We can do that in one of two ways: either with client-side JavaScript, or using an HTML <Meta> tag. They both work on most modern browsers. A real expert would probably come up with reasons why one should be used in preference to another. Since I'm not an expert, for me the choice was very easy: why learn JavaScript when the HTML tag is so easy to implement? The syntax of the HTML tag is
<meta http-equiv="refresh" content="5; URL=mypage.a5w”>
which simply tells the page to wait 5 seconds and then redirect itself to the page “mypage.a5w”. This <Meta> tag must be placed in the <head> section of the page.
Using this meta tag, this next script shows the server's time on the user's browser and updates itself every few seconds:
<%a5
' Timer.a5w
select
case eval_valid("count")=.f.
html=<<%txt%
<html><head><title>Timer</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+<<%txt%
?count=1">
</head>
<body>
<P>The time is now %txt%+\
time("h:0m:0s am")+"</P></body></html>"
case val(count)<20
count=alltrim(str(val(count)+1))
html=<<%txt%
<html><head><title>Timer</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+\
"?count="+count+<<%txt%
">
</head>
<body>
<P>The time is now %txt%+\
time("h:0m:0s am")+"</P></body></html>"
case else
html=<<%txt%
<html><head><title>Timer</title>
</head>
<body>
<P>Timer completed at %txt%+\
time("h:0m am")+"</P></body></html>"
end select
? html
%><meta content="MSHTML 6.00.2800.1106" name=GENERATOR></head>
<body></body></html>Script 2. “Timer.a5w”
If you publish this script and then access it from your browser, this is what you see:

Don't
blame me, I didn't say it would be exciting. But it's a start.
Step 3. Incremental improvement
The next script in our incremental approach starts a server-side task that runs for a period and whose progress can be monitored from the client side every few seconds. In this script I use a new v6 method, thread_create(), which creates a background task. The particular thread I create in this script doesn't do anything special, but in a real application you would use the thread to execute a task or a series of tasks. Notice I also use the methods, thread_is_running() and thread_variables() to check on the background task. These methods are all documented in the A5v6 help file:
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html><head>
<meta http-equiv=Content-type content="text/html; charset=unicode">
<%a5
' Counter.a5w
select
case thread_is_running("background")
bptr=thread_variables("background")
html=<<%txt%
<html><head><title>Counter</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+\
"?count="+bptr.count+<<%txt%
">
</head>
<body>
<P>You have now completed %txt%+\
round(100*(bptr.count-bptr.start)/(bptr.maxcount-bptr.start),1)+\
"% of the task</P></body></html>"
case eval_valid("count")=.f.
html=<<%txt%
<html><head><title>Counter</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+<<%txt%
?count=1">
</head>
<body>
<P>You are now beginning the task</P></body></html>%txt%
thread_create("background",<<%code%
maxcount=1000
start=1
for count=start to maxcount
sleep(0.1)
next
%code%)
case else
html=<<%txt%
<html><head><title>Done</title>
</head>
<body>
<P>You have now completed the entire task</P></body></html>
%txt%
end select
? html
%><meta content="MSHTML 6.00.2800.1106" name=GENERATOR></head>
<body></body></html>Script 3. “Counter.a5w”
Now we're getting somewhere. If you open this page from your browser, you'll see:

And
this message gets updated as the task runs to completion.
Step 4. Add the Progress Bar
The final step is to add a progress bar. You can find a whole slew of JavaScript progress bars through a Google search, but you don't need to know JavaScript. All we need is one large image containing a solid color, and then we can use HTML <img> tags to format the image to the right size to show progress. Open up the Paint application on your computer, select the entire Paint window, and fill it with blue. Then save your artistic endeavor to your web application's folder as “blue.gif”. You'll now have a blue image that you can cut and trim to use as a progress bar.

Microsoft
Paint with the working area filled in with blue.
Save your art work as “blue.gif”:

And now all we do is incorporate code to show the image at different sizes as the task progresses:
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html><head>
<meta http-equiv=Content-type content="text/html; charset=unicode">
<%a5
select
case thread_is_running("background")
bptr=thread_variables("background")
html=<<%txt%
<html><head><title>Progress</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+\
"?count="+bptr.count+<<%txt%
">
</head>
<body>
<P>You have now completed %txt%+\
round(100*(bptr.count-bptr.start)/(bptr.maxcount-bptr.start),1)+\
"% of the task</P>"+<<%txt%
<p><img src="blue.gif" height=30 width=%txt%+\
200*(bptr.count-bptr.start)/(bptr.maxcount-bptr.start)+\
"></P></body></html>"
case eval_valid("count")=.f.
html=<<%txt%
<html><head><title>Progress</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+<<%txt%
?count=1">
</head>
<body>
<P>You are now beginning the task</P></body></html>%txt%
thread_create("background",<<%code%
maxcount=1000
start=1
for count=start to maxcount
sleep(0.1)
next
%code%)
case else
html=<<%txt%
<html><head><title>Done</title>
</head>
<body>
<P>You have now completed the entire task</P></body></html>
%txt%
end select
? html
%><meta content="MSHTML 6.00.2800.1106" name=GENERATOR></head>
<body></body></html>Script 4. “Progress.a5w”
Running this page from the browser is a bit more impressive:

and
checking later shows us:

With
a little effort, you can make a more attractive progress meter. This
should get you started.
Step 5. Terminating the Task.
What if your user starts a tasks and realizes she didn't want to run it after all? She could close the browser window but the background task will continue to run, consuming server resources. Your a5w page can communicate with the background thread by manipulating the thread's variables. (Threads also have events which would probably make this code easier, but as of this writing they are undocumented—hint to Alpha Software, please?). This final script lets the user stop the background thread:
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html><head>
<meta http-equiv=Content-type content="text/html; charset=unicode">
<%a5
select
case eval_valid("count")=.f.
html=<<%txt%
<html><head><title>Progress2</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+<<%txt%
?count=1">
</head>
<body>
<P>You are now beginning the task</P></body></html>
%txt%
thread_create("background",<<%code%
maxcount=1000
start=1
for count=start to maxcount
sleep(0.1)
next
%code%)
case thread_is_running("background")
bptr=thread_variables("background")
if val(count)>bptr.maxcount then 'user pressed "terminate" button
bptr.count=bptr.maxcount
end if
html=<<%txt%
<html><head><title>Progress2</title>
<meta http-equiv="refresh" content="3; URL=%txt%+request.script_name+\
"?count="+bptr.count+<<%txt%
">
</head>
<body>
<P>You have now completed %txt%+\
round(100*(bptr.count-bptr.start)/(bptr.maxcount-bptr.start),1)+\
"% of the task</P>"+<<%txt%
<p><img src="blue.gif" height=30 width=%txt%+\
200*(bptr.count-bptr.start)/(bptr.maxcount-bptr.start)+\
"><form action="+request.script_name+"?count="+bptr.maxcount+1+" method=post>"+\
"<input type=submit name=OK value=Terminate></form></P></body></html>"
case else
html=<<%txt%
<html><head><title>Done</title>
</head>
<body>
<P>You have now completed the entire task</P></body></html>
%txt%
end select
? html
%><meta content="MSHTML 6.00.2800.1106" name=GENERATOR></head>
<body></body></html>Script 5. “Progress2.a5w” -- a progress bar with a Terminate button.
This is what the user sees:

Pressing
the “Terminate” button stops the background task by
setting the thread's “count” variable to 1001. In this
particular example, the user does not get any special message that
the task was terminated prematurely; that's an exercise left to you,
dear reader!
1/15/2005
Don't forget, we need your feedback to make this site better!