简体   繁体   中英

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?

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM