Bartosz
Bartosz

Reputation: 4766

while loop during awaiting for async task

I have a following problem. I have a synchronous .Save() action that takes quite a few minutes to complete. I cannot make it async (3rd party library), so to keep the UI responsive, I am using it all in Task.Run().

I would like to report progress so that the users sees that something is happening - and I want to make it in the Status label - a simple and old school append and remove dots to string, sort of like
Wait.
Wait..
Wait...
Wait.

I don't know how to do it in a simple way - I thought of a while loop that would break once the Task ends, but it doesn't seem to work - either the UI is blocked or the status label does not update.

I tried a couple of things, for example:

bool done = false;
            Task.Run(() =>
            {
                Exporter.DoLongSyncTask();
            }).ContinueWith(r => done = true);
            StatusLabel = ".";
            while (!done)
            {
                if (StatusLabel == "")
                    StatusLabel = ".";
                else if (StatusLabel == ".")
                    StatusLabel = "..";
                else if (StatusLabel == "..")
                    StatusLabel = "";
            }

(StatusLabel is a property with INotifyPropertyChanged implemented, it works OK)

and more complicated

private async Task<bool> DoTheWork()
        {

            await Task.Run(() => TheFile.Save());
            return true;
        }

            private string ReportProgress(string status)
        {
            if (status == ".")
                status = "..";
            else if (status == "..")
                status = "...";
            else if (status == "...")
                status = ".";
            MyProgress.Report(new InDesignExporterProgress { StatusInfo = status });
            return status;
        }

private string ReportProgress(string status)
{
    if (status == ".")
        status = "..";
    else if (status == "..")
        status = "...";
    else if (status == "...")
        status = ".";
    MyProgress.Report(new SomeProgressClass { StatusInfo = status });
    return status;
}       

and the above are called from here

        bool done = false;
        Task.Run(() =>
        {
            done = DoTheWork().Result;
        });
        string status = ".";
        while (!done)
        {
            status = ReportProgress(status);
        }

So, what's the right way?

Upvotes: 1

Views: 12162

Answers (2)

Cyrus
Cyrus

Reputation: 344

I think you are blocking your UI thread with the while loop since I guess it's running on that thread.

I suggest you try to change your while loop like this and the method signature to async Task

public async Task DoSomething(){
        bool done = false;
        Task.Run(() =>
        {
            Exporter.DoLongSyncTask();
        }).ContinueWith(r => done = true);

        StatusLabel = ".";
        while (!done)
        {
            if (StatusLabel == "")
                StatusLabel = ".";
            else if (StatusLabel == ".")
                StatusLabel = "..";
            else if (StatusLabel == "..")
                StatusLabel = "";
            await Task.Delay(200);
        }
}

Upvotes: 0

Paulo Morgado
Paulo Morgado

Reputation: 14846

To know if a task has been completed, check the value of the IsCompleted property.

while (!task.IsCompleted)
{
    // ...
}

But, for what I think you're trying to, I'd do something like this:

using (var timer = new System.Windows.Forms.Timer())
{
    timer.Tick += (s, e) =>
    {
        if (StatusLabel == "")
            StatusLabel = ".";
        else if (StatusLabel == ".")
            StatusLabel = "..";
        else if (StatusLabel == "..")
            StatusLabel = "";
    };
    timer.Interval = 100;
    timer.Enabled = true;

    await Task.Run(() => Exporter.DoLongSyncTask);
}

Upvotes: 4

Related Questions