title: How to cancel Task await after a timeout period
body: <p>I am using this method to instantiate a web browser programmatically, navigate to a url and return a result when the document has completed.</p>
  
  <p>How would I be able to stop the <code>Task</code> and have <code>GetFinalUrl()</code> return <code>null</code> if the document takes more than 5 seconds to load?</p>
  
  <p>I have seen many examples using a <code>TaskFactory</code> but I haven't been able to apply it to this code.</p>
  
  <pre><code> private Uri GetFinalUrl(PortalMerchant portalMerchant)
      {
          SetBrowserFeatureControl();
          Uri finalUri = null;
          if (string.IsNullOrEmpty(portalMerchant.Url))
          {
              return null;
          }
          Uri trackingUrl = new Uri(portalMerchant.Url);
          var task = MessageLoopWorker.Run(DoWorkAsync, trackingUrl);
          task.Wait();
          if (!String.IsNullOrEmpty(task.Result.ToString()))
          {
              return new Uri(task.Result.ToString());
          }
          else
          {
              throw new Exception("Parsing Failed");
          }
      }
  
  // by Noseratio - http://stackoverflow.com/users/1768303/noseratio    
  
  static async Task&lt;object&gt; DoWorkAsync(object[] args)
  {
      _threadCount++;
      Console.WriteLine("Thread count:" + _threadCount);
      Uri retVal = null;
      var wb = new WebBrowser();
      wb.ScriptErrorsSuppressed = true;
  
      TaskCompletionSource&lt;bool&gt; tcs = null;
      WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =&gt; tcs.TrySetResult(true);
  
      foreach (var url in args)
      {
          tcs = new TaskCompletionSource&lt;bool&gt;();
          wb.DocumentCompleted += documentCompletedHandler;
          try
          {
              wb.Navigate(url.ToString());
              await tcs.Task;
          }
          finally
          {
              wb.DocumentCompleted -= documentCompletedHandler;
          }
  
          retVal = wb.Url;
          wb.Dispose();
          return retVal;
      }
      return null;
  }
  
  public static class MessageLoopWorker
  {
      #region Public static methods
  
      public static async Task&lt;object&gt; Run(Func&lt;object[], Task&lt;object&gt;&gt; worker, params object[] args)
      {
          var tcs = new TaskCompletionSource&lt;object&gt;();
  
          var thread = new Thread(() =&gt;
          {
              EventHandler idleHandler = null;
  
              idleHandler = async (s, e) =&gt;
              {
                  // handle Application.Idle just once
                  Application.Idle -= idleHandler;
  
                  // return to the message loop
                  await Task.Yield();
  
                  // and continue asynchronously
                  // propogate the result or exception
                  try
                  {
                      var result = await worker(args);
                      tcs.SetResult(result);
                  }
                  catch (Exception ex)
                  {
                      tcs.SetException(ex);
                  }
  
                  // signal to exit the message loop
                  // Application.Run will exit at this point
                  Application.ExitThread();
              };
  
              // handle Application.Idle just once
              // to make sure we're inside the message loop
              // and SynchronizationContext has been correctly installed
              Application.Idle += idleHandler;
              Application.Run();
          });
  
          // set STA model for the new thread
          thread.SetApartmentState(ApartmentState.STA);
  
          // start the thread and await for the task
          thread.Start();
          try
          {
              return await tcs.Task;
          }
          finally
          {
              thread.Join();
          }
      }
      #endregion
  }
  </code></pre>