mnj
mnj

Reputation: 3413

How to make a method asynchronous when I need to wait for an event

I made a function like this:

public void GetData(string dataToPost)
{
    var url = "some URL";

    using (var client = new WebClient())
    {
        client.Headers[HttpRequestHeader.ContentType] = "application/json";

        client.UploadStringCompleted += (s, e) =>
            {
                Console.WriteLine("Result is here");
                Console.WriteLine(e.Result);
            };

        client.UploadStringAsync(new System.Uri(url), "POST", dataToPost);
    }
}

The problem here is the fact that I want to get a value returned from the server (HTTP response).

I want my function to be asynchronous, so for example I'd like to be able to call it 5 times in a loop, without waiting for each call to return something before next call.

I have no idea how to achieve it. I tried to create some Task object to await it, but Task requires a delegate and I don't know what delegate i could give it.

Te function above uses an event, which is fired when the result comes - so I need to return that result.

How can I do that?

Upvotes: 0

Views: 154

Answers (2)

VMAtm
VMAtm

Reputation: 28355

As already been said in comments, HttpClient is more natural choice for such tasks. However, you still may wonder how to provide a task subscribed for an event, and that could be done with TaskCompletionSource<T>, with quite small changes to your code:

private Task<string> GetData(string dataToPost)
{
    var url = "some URL";
    var resultSource = new TaskCompletionSource<string>();

    using (var client = new WebClient())
    {
        client.Headers[HttpRequestHeader.ContentType] = "application/json";

        client.UploadStringCompleted += (s, e) => {
            Console.WriteLine("Result is here");
            Console.WriteLine(e.Result);

            // this will complete the task
            resultSource.SetResult(e.Result);
        };

        client.UploadStringAsync(new System.Uri(url), "POST", dataToPost);
    }

    return resultSource.Task;
}

You can also set a cancellation (with a given token too) and exception (even with multiple exceptions) in this case, so it will naturally fit to your needs. All three methods can be done in Try* fashion for the cases of concurrent event subscription.

Also note the @Stefanod'Antonio' answer for an async method overload.

Upvotes: 1

Stefano d&#39;Antonio
Stefano d&#39;Antonio

Reputation: 6152

WebClient has an overload that returns a Task<string>:

https://msdn.microsoft.com/en-us/library/hh193920(v=vs.110).aspx

You can fire all your requests and then wait for them with Task.WhenAll:

public void GetData(string dataToPost)
{
    var url = "some URL";
    var url2 = "some other URL";

    using (var client = new WebClient())
    {
        client.Headers[HttpRequestHeader.ContentType] = "application/json";

        var task1 = client.UploadStringTaskAsync(new System.Uri(url), "POST", dataToPost);
        var task2 = client.UploadStringTaskAsync(new System.Uri(url2), "POST", dataToPost);

        var results = Task.WhenAll(task1, task2).Result;

        foreach (var result in results)
        {
            Console.WriteLine("Result is here");
            Console.WriteLine(result);
        }
    }
}

Even better if you can change your GetData to be an async method, you can just await on Task.WhenAll

Upvotes: 2

Related Questions