In the OnDisconnectedAsync
event of my hub, I want to wait a few second before performing some action. I tried to make it async for using non-blocking Task.Delay
:
public override async Task OnDisconnectedAsync(Exception exception) {
var session = (VBLightSession)Context.Items["Session"];
activeUsers.Remove(session.User.Id);
await Task.Delay(5000);
if(!activeUsers.Any(u => u.Key == session.User.Id)) {
await Clients.All.SendAsync("UserOffline", UserOnlineStateDto(session));
}
await base.OnDisconnectedAsync(exception);
}
While this works as expected, I noticed that I can’t close the console application immediately. Seems that it waits for the 5 seconds delay to finish. How can I solve this, that exiting the application simply exite those delay, too?
The only alternative I see is Creating a classic thread and inject IHubContext
, but this seems not scaling well and some kind of overkill for this simple task.
Backgrund
I have a list of online users. When users navigating through the multi page application, they get disconnected for a short time during the new HTTP request. To avoid such flickering in the online list (user get offline and directly online again), I want to remove the user on disconnect from the user list, but not notify the WS client immediatly.
Instead I want to wait 5 seconds. Only if the client is still missing in the list, I know the client hasn’t reconnected and I notify the other users. For this purpose, I need to sleep on the disconnect event. The above solution works well, except the delay on application exit (which is annoying during development).
A single page application like Angular or other frameworks shouldn’t be used for a few reasons, mainly performance and SEO.
2
Answers
I learned about the CancellationToken, which could be passed to
Task.Wait
. It could be used to abort the task. Creating such a token usingCancellationTokenSource
seems good to cancel the token programatically (e.g. on some condition).But I found the ApplicationStopping token in the
IApplicationLifetime
interface, that requests cancellation when the application is shutting down. So I could simply injectand only sleep if no cancellation is requested from this token
This works because when closing the application, SignalR detects this as disconnect (altough it's caused from the server). So he waits 5000s before exit, like I assumed in my question. But with the token,
IsCancellationRequested
is set to true, so no additional waiting in this case.I would not have a problem with the program waiting the X seconds before close, because it is granted that no operation will stays in the middle of the operation.
For example, what if the operation was doing something in the database? IT could leave the connection open.
I would introduce a
CancellationToken
, and, instead of waiting 5 seconds, wait 1 or less and check againAnd then
Then you can add
IApplicationLifetime
to let the app know when the signal to shit down comes and cancel your CancellationToken.You could even get this code in other class and generalize it to use it in other places.