Question Using Task.WhenAll with timeout

bczm8703

Member
Joined
Jan 21, 2014
Messages
7
Programming Experience
1-3
i have a mobile app code that gets data from a remote web service.

i have set the task to get data and update the corresponding UI elements in various tabs

This is my current code
C#:
var doughnutData = GetDoughnutDataAsync(filters);
var barData = GetBarDataAsync(filters);
var trendlineData = GetTrendLineDataAsync(filters);
var categoryData = GetCategoryDataAsync(filters);
var clientData = GetClientDataAsync(filters);
await Task.WhenAll(doughnutData, barData, trendlineData, categoryData, clientData);

From the above code, there are 5 tasks corresponding to 5 tabs in the app

while the tasks are running, I will show a loading icon in all 5 of the tabs. Once the task is done, the loading icon of the said task will be hidden

Everything is running fine, but there will be random times where either the server or some other stuff causing the task to run very long.

What I want is, to have a timeout after maybe 2mins, if the task is not completed I want to show a connection issue message at the corresponding tabs.
 
The quick and dirty way would be something like this:
C#:
var timeOut = TimeSpan.FromMinutes(2);
:
var doughnutData = Task.WhenAny(GetDoughnutDataAsync(filters), Task.Delay(timeOut));
: // repeat same pattern above for the other tasks

// then do like you did previously
await Task.WhenAll(doughnutData, barData, trendlineData, categoryData, clientData);

A better way would be to makes sure that you various GetFooDataAsync methods all take a cancellation token parameter. Then you would write something like:
C#:
var timeOut = TimeSpan.FromMinutes(2);
var cancellationTokenSource = new CancellationTokenSource(timeOut);
var token = cancellationTokenSource.Token;
:
var doughnutData = GetDoughnutDataAsync(filters, token);
: // repeat same pattern above for the other tasks

// then do like you did previously
await Task.WhenAll(doughnutData, barData, trendlineData, categoryData, clientData);
 
Another quick and dirty variation:
C#:
var timeOut = TimeSpan.FromMinutes(2);
await Task.WhenAny(Task.WhenAll(doughnutData, barData, trendlineData, categoryData, clientData),
                   Task.Delay(timeOut));
 
Hi. thanks for replying. but how do i know which task did not complete successfully so that i can update the UI element with the message?

it might happen whereby only the task trendlineData did not complete before the timeout, etc
 
Hi.

i tried using this but seems that only doughnut task is completed. the remaining task seems like the UI element got update half way and got interrupted.

Code
C#:
var timeOut = TimeSpan.FromMinutes(2);

var doughnutData = GetDoughnutDataAsync(filters);
var barData = GetBarDataAsync(filters);
var trendlineData = GetTrendLineDataAsync(filters);
var categoryData = GetCategoryDataAsync(filters);
var clientData = GetClientDataAsync(filters);

var doughnutTask = Task.WhenAny(doughnutData, Task.Delay(timeOut));
var barTask = Task.WhenAny(barData, Task.Delay(timeOut));
var trendlineTask = Task.WhenAny(trendlineData, Task.Delay(timeOut));
var categoryTask = Task.WhenAny(categoryData, Task.Delay(timeOut));
var clientTask = Task.WhenAny(clientData, Task.Delay(timeOut));

await Task.WhenAll(doughnutTask, barTask, trendlineTask, categoryTask, clientTask);


if(!doughnutData.IsCompleted)
{
     doughnutBusyIndicator.IsBusy = false;
     doughnutBusyIndicator.IsVisible = false;
     lblSlaAchivementError.IsVisible = true;
     lblSlaAchivementError.Text = "Connection to server has timeout";
 }

if (!barData.IsCompleted)
{
     barBusyIndicator.IsBusy = false;
     barBusyIndicator.IsVisible = false;
     lblDeliveryError.IsVisible = true;
     lblDeliveryError.Text = "Connection to server has timeout";
 }

if (!trendlineData.IsCompleted)
 {
       trendlineBusyIndicator.IsBusy = false;
       trendlineBusyIndicator.IsVisible = false;
       lblTrendlineError.IsVisible = true;
       lblTrendlineError.Text = "Connection to server has timeout";
}

if (!categoryData.IsCompleted)
{
     categoryBusyIndicator.IsBusy = false;
     categoryBusyIndicator.IsVisible = false;
     lblCategoryError.IsVisible = true;
     lblCategoryError.Text = "Connection to server has timeout";
}

if (!clientData.IsCompleted)
{
       clientBusyIndicator.IsBusy = false;
       clientBusyIndicator.IsVisible = false;
       lblClientDetailError.IsVisible = true;
       lblClientDetailError.Text = "Connection to server has timeout";
}

TaskIssue.jpg



When the task is completed, the red box area suppose to be hidden, a chart suppose to be render and the black box area to be shown.

Currently the black box area is shown but the red box did not disappear and the chart is not shown.
 
I would likely do code from post #6 like:
C#:
class Element
{
    public Task Task;
    public BusyIndicator BusyIndicator;
    public Label Label;

    void ShowTimedOut()
    {
        BusyIndicator.IsBusy = false;
        BusyIndicator.IsVisible = false;
        Label.IsVisible = true;
        Label.Text = "Connection to server has timed out."
    }

    public void Update()
    {
        if (!Task.IsCompleted)
            ShowTimedOut();
    }
}
:

var elements = new List<Element>()
{
    new Element()
    {
        Task = GetDoughnutDataAsync(filters),
        Indicator = doughnutBusyIndicator,
        Label = lblSlaAchievementError
    },
    :
};

var timeOut = TimeSpan.FromMinutes(2);
await Task.WhenAll(elements.Select(e => Task.WhenAny(e.Task, Task.Delay(timeOut))));

foreach(var element in elements)
    element.Update();

Or if I deemed that the ContinueWith() was a non-issue:
C#:
class Element
{
    public Task Task;
    public BusyIndicator BusyIndicator;
    public Label Label;

    public void ShowTimedOut()
    {
        BusyIndicator.IsBusy = false;
        BusyIndicator.IsVisible = false;
        Label.IsVisible = true;
        Label.Text = "Connection to server has timed out."
    }
}
:

var elements = new List<Element>()
{
    new Element()
    {
        Task = GetDoughnutDataAsync(filters),
        Indicator = doughnutBusyIndicator,
        Label = lblSlaAchievementError
    },
    :
};

var timeOut = TimeSpan.FromMinutes(2);
await Task.WhenAll(elements.Select(e => 
                                   Task.WhenAny(e.Task,
                                                Task.Delay(timeOut)
                                                    .ContinueWith(_ => e.ShowTimedOut()))));
 
Back
Top Bottom