Add tasks to WhenAny asynchronously

146 Views Asked by At

I'm looking to listen to the result of a task(s), or timeout if the none are made or completed. Specifically, I'm using Playwright and am adding an event listener for requests and want to timeout if none of those requests include what I'm looking for (or even get created). Example code below:

I've tried looking into WhenAny, but that requires knowing the tasks ahead of time. Also looked into Monitor.TryEnter, but not sure how to have the requests acquire the lock before they're generated? Maybe a semaphore or something?

string? result = null;

// Start listening for new pages (ClickAsync below opens in a new page)
context.Page += (_, newPage) =>
{
  // From the new page, start listening for requests that page generates
  newPage.Request += async (_, request) =>
  {
    if (request.Url.Contains("some_identifier"))
    {
      await newPage.GotoAsync(request.Url);
      result = await newPage.InnerTextAsync("body");
      // Do something here? We now have the result we want, stop waiting and return it!
    }
  };
};

await mainPage.Locator("a#somelink").ClickAsync();

// Wait for *up to* 30 seconds here.

return result;
1

There are 1 best solutions below

0
On

Using ideas from comments on my answer I was able to accomplish the goal. I ended up passing a cancellation token to a Task.Delay(30000, token). Then if I got the data I wanted and set the result value, I just cancelled the token.

string? result = null;
var tokenSource = new CancellationTokenSource();
var cancelToken = tokenSource.Token;

// Start listening for new pages (ClickAsync below opens in a new page)
context.Page += (_, newPage) =>
{
  // From the new page, start listening for requests that page generates
  newPage.Request += async (_, request) =>
  {
    if (request.Url.Contains("some_identifier"))
    {
      await newPage.GotoAsync(request.Url);
      result = await newPage.InnerTextAsync("body");
      tokenSource.Cancel(); // <-- Got what we needed, cancel 30 second timer
    }
  };
};

await mainPage.Locator("a#somelink").ClickAsync();

try {
  await Task.Delay(30000, cancelToken); // <-- Timeout after 30 seconds.
}
catch
{
  // Not a fan of tokenSource.Cancel() throwing an exception, but I can deal with it.
}

return result;