Before my app closes, I want it to wait for all URLConnection requests to return.
I have a list of queued POSTs, each of which need to wait for the ContentReceived event before sending the next, so I want to wait until the queued posts have sent and responses returned (with a reasonable timeout) before the program closes.
Works fine while the program is doing things, but when it’s time to quit, I need the program to wait until the POSTs are done and responses received (or a reasonable timeout has been reached) before closing the program.
Thank you, Ivan;
“Call a timer”? Can you post an example? I understand what you mean, but not quite sure how to implement.
I was thinking that the wait would be done in the App.CancelClose event…
if it was just a busy loop then yes that would likely have that effect
In Cancel Close if you determine there are connections waiting to complete
if connectionsNeedToComplete then
dim t as new timer
addhandler t.action, addressof timerhandler
t.run // or whatever it is they are calling it now
return true to cancel closing // you might close windows etc
end if
the method you attach the timer to simply runs periodically and when all connections have completed it calls quit
What do I put in TimerHandler, and how do I get the program to wait (without blocking events) until all the events have completed (or a timeout is reached)?
in the handler ?
whatever code you need to periodically check and see if all connections are finished doing their thing
I have no means to offer anything more than the advice
and IF they are all done then you just call quit
the timer itself will simply run & continue to run until you call quit
events will still happen
the timer
set it up to fire continuously every 250 msec and then when you figure out all connections are complete call QUIT
the app will NOT quit when cancel close is called because you return true (ie/ DO NOT CLOSE)
events will still happen just like always because the main loop of the app is still running
adjust as necessary
theres a method I just hard wired to say there is work left to do
adjust that to your code to check if downloads are still pending and do not quit until they are all done
you might consider
hiding all windows when the app goes into this waiting state
disabling all menu items since the app is going to do its downloading and then quit
Hey @npalardy , I had a look at your sample, but it’s not quite the same, I think your example is just doing a slow process rather than waiting for events. My issue seems to be that events are being blocked.
Here’s the code that needs to be “fixed” by removing DoEvents. Any suggestions? LogSocket.Messages is the queue of waiting requests to URLConnection. Each time the URLConnection.ContentReceived event fires, a message is removed from the queue.
This procedure is called in CancelClose:
Splash.Show
App.DoEvents // Splash screen will not appear without this
If LogSocket <> Nil Then
FlushTimer = New Timer
AddHandler FlushTimer.Action, AddressOf UpdateMsgCount
FlushTimer.Period = 50
FlushTimer.Mode = Timer.ModeMultiple
FlushTimeout = 0
Flushes = LogSocket.Messages.UBound
While LogSocket.Messages.UBound > 0 And FlushTimeout < 100
App.DoEvents // nothing happens without this
Wend
FlushTimer.Mode = Timer.ModeOff
FlushTimer = Nil
End If
And here’s UpdateMsgCount:
If Splash.Visible Then
If Flushes > LogSocket.Messages.UBound Then Splash.TextField1.Text = "Closing Program... " + Str(LogSocket.Messages.UBound, "000")
FlushTimeout = FlushTimeout + 1
Else
FlushTimer.Mode = Timer.ModeOff
FlushTimeout = 100
Splash.Close
End If
Gotcha.
So start the timer in CancelClose and return true
Check whether the message queue is empty (or the timer has run long enough) in the timer action
When the queue is empty or the the timer has run long enough call Quit
So will Quit call CancelClose again or go straight to Close?
If it calls CancelClose, then I’ll need to have a flag in the timer action that says we’re done.
I see now that’s what you were doing, so I’ll try again.
Thanks, @npalardy , got it working now without any doevents. My real issue was not realizing that cancelclose stops all events if it completes. It’s documented but I must have missed that, thinking that app.close would be where all events stop.
Private Sub TriggerQuit()
Quit
End Sub
Event CancelClose()
If PendingConnections Then
Timer.CallLater(2000, WeakAddressOf TriggerQuit)
Return True
End If
End Event
Assuming PendingConnections is something that returns true if there are connections that have not finished yet. This way when you call Quit, it’ll check if there are connections running. If there are, the quit will be cancelled and attempted again 2 seconds later.
You could show UI here as long as you keep track of wether or not the UI you need has been destroyed.
Personally I avoid CallLater like the plague
Mostly because if the object that holds the method being called later goes out of scope (like say a window) then you just crash unceremoniously
Yes, there are plenty of ways to shoot yourself in the foot with CallLater. Sure would be nice if that could be checked for before trying to fire the delegate.
In this case, it’s safe because App is never going out of scope. The only problem remaining with this use of CallLater is its tendency to fire a seemingly-random “dictionary was changed while iterating over it” exception.