myro
myro

Reputation: 1196

Async method with an async lambda expression

I have a lambda expression with an async call inside

public async Task UploadFile(string id)
{
    Progress<double> progress = new Progress<double>(async x =>
    {
        await Clients.Client(id).SendAsync("FTPUploadProgress", x);
    });
    await client.DownloadFileAsync(localPath, remotePath, true, FluentFTP.FtpVerify.Retry, progress);
}

I want to call the async method when progress is changed.

I'm getting the following warning:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API call

How can I make this method asynchronous?

Should I rewrite the lambda with a System.Action<T> class?

Upvotes: 0

Views: 4902

Answers (2)

Nkosi
Nkosi

Reputation: 247088

Use an event handler and raise it in the progress action. the event handler will allow async calls.

Luckily Progress<T> has a ProgressChanged event that you can subscribe to.

Review the following example based on the presented code in original problem

public async Task UploadFile(string id) {
    EventHandler<double> handler = null;
    //creating the handler inline for compactness
    handler = async (sender, value) => {
        //send message to client asynchronously
        await Clients.Client(id).SendAsync("FTPUploadProgress", value);
    };
    var progress = new Progress<double>();
    progress.ProgressChanged += handler;//subscribe to ProgressChanged event

    //use the progress as you normally would
    await client.DownloadFileAsync(localPath, remotePath, true, FluentFTP.FtpVerify.Retry, progress);

    //unsubscribe when done 
    progress.ProgressChanged -= handler;
}

So now when progress is reported, the event handler can make the asynchronous call.

Reference Async/Await - Best Practices in Asynchronous Programming

Another option would be to create you own IProgress<T> implementation that takes a Func<Task<T>> that would allow for async calls, but that might be overkill.

Upvotes: 4

Pretasoc
Pretasoc

Reputation: 1126

I think you missunderstood the usage of the Progress<T> class. The Compiler complains, that your Method UploadFile lacks an await operator. Your lambda would execute asynchronous, when it would be called.

So heres a short Summary how to use the IProgressyT> interface:

If you have a Method that should support reporting of Progress, it can take an IProgress<T> as an Parameter and communicate its porgress through this object. It does not perform the monitored Operation. The Lambda you supply is executed each time the Report() Method is called on the Progress<T>. This Lambda usually is used to update the UI.

Here is an example for that.

public async Task DoStuff(IProgress<double> progress = null)
{
     for(int i = 0; i < 100; ++i)
     {
         await Task.Delay(500);
         progress?.Report((double)(i +1) / 100);
     }
}


// somewhere else in your code
public void StartProgress(){
    var progress = new Progress(p => Console.WriteLine($"Progress {p}"));
    DoStuff(progress);  
}

Upvotes: 2

Related Questions