Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I instantly return values from an async loop?

I've got an async discover function which discovers devices in the local network which I call on a button click. I'm sending a broadcast message to all devices and listen for responses for 5 seconds using a CancellationTokenSource. After the token has expired I'm returning an IEnumerable of parsed responses to my WPF model.

I'd like return incoming responses directly (and stop listening after 5 seconds) so that I can show discovered devices instantly in the UI instead of showing them all after 5 seconds.

This is my code:

public async Task<IEnumerable<IDevice>> Discover()
{
    var client = new MyClient();
    var responseData = await GetResponseData(client);
    return this.ParseResponseData(responseData);
}

private IEnumerable<IDevice> ParseResponseData(List<DeviceResponseData> responseData)
{
    foreach (var data in responseData)
    {
        yield return DeviceFactory.Create(data);
    }
}

private static async Task<List<DeviceResponseData>> GetResponseData(MyClient client,
    int timeout = 5000)
{
    var cancellationToken = new CancellationTokenSource(timeout);
    var data = new List<DeviceResponseData>();

    // ... prepare message and send it
    await client.SendAsync(message, new CancellationToken());

    try
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // Wait indefinitely until any message is received.
            var response = await client.ReceiveAsync(cancellationToken.Token);

            data.Add(new DeviceResponseData(/* ... */ response));
        }
    }
    catch (TaskCanceledException e)
    {

    }

    return data;
}
like image 521
Brian O Avatar asked Dec 06 '25 06:12

Brian O


1 Answers

If you want to show results as they come in, there are many ways of achieving this, like decoupled messages, events, etc.

However, you could just use a simple Action

private static async Task<List<DeviceResponseData>> GetResponseData(MyClient client, Action<DeviceResponseData> update, int timeout = 5000)
{
   var cancellationToken = new CancellationTokenSource(timeout);
   ...
   while (!cancellationToken.IsCancellationRequested)
   {
      // Wait indefinitely until any message is received.
      var response = await client.ReceiveAsync(cancellationToken.Token);

      var result = new DeviceResponseData( /* ... */ response);

      data.Add(result);
      update(result);

   }
   ...
}

usage

var allResults =  await GetResponseData(client,data => UdpateUI(data), timeout);

Note : because this the Async Await Pattern, you wont have to marshal the result form the Action back to the UI Context, if that's where this was called from.

like image 62
TheGeneral Avatar answered Dec 08 '25 19:12

TheGeneral



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!