title: Best practice for async/await methods and exception handling
body: <p>I have to work on some old stuff not correctly functionning using async functions, and as my understanding of this concept is limited I'm struggling on finding the best way to handle the following issue.</p>
  
  <p>I have a button, which when clicked will perform a long running job (unzipping large ZIP archive, taking a few minutes). Its <code>Command</code>execute method is defined like this :</p>
  
  <pre><code>private async void Import()
  {
      // some stuff
      tokenSource = new CancellationTokenSource();
      IProgress&lt;ProgressReport&gt; progress = new Progress&lt;ProgressReport&gt;(data =&gt; Report(data));
  
      try
      {
          await Task.Run(() =&gt;
          {
              Backup(tokenSource.Token, progress);
              Unzip(tokenSource.Token, progress);
          });
      }
      catch(Exception)
      {
          // do some rollback operation
      }
  </code></pre>
  
  <p>And the awaited functions defined like this :</p>
  
  <pre><code>private void Backup(CancellationToken token, IProgress&lt;ProgressReport&gt; progress)
  {
  token.ThrowIfCancellationRequested();
  
      var parent = Path.GetFullPath(Path.Combine(Paths.DataDirectory, ".."));
      if (!Directory.Exists(parent))
      {
          progress.Report(new ProgressReport(Level.Info, string.Format(
                  "Root Directory ({0}) does not exist; Creating it.", parent)));
          Directory.CreateDirectory(parent);
          return;
      }
  
      if (!Directory.Exists(Paths.DataDirectory))
      {
          progress.Report(new ProgressReport(Level.Info, string.Format(
                  "Data Directory ({0}) does not exist; nothing to backup.", Paths.DataDirectory)));
          return;
      }
  
      // Generate a name for the backup           
      try
      {
          progress.Report(new ProgressReport(Level.Info, string.Format(
                  "Renaming source Data Directory ({0}) to a temporary name.", Paths.DataDirectory)));
  
          var temp = Path.Combine(parent, Guid.NewGuid().ToString("N"));
          Directory.Move(Paths.DataDirectory, temp);
          // Success, let's store the backupFolder in a class field
          backupFolder = temp;
  
          progress.Report(new ProgressReport(Level.Info, string.Format(
                  "Source Data Directory ({0}) successfully renamed to {1}.", Paths.DataDirectory, backupFolder)));
  
          token.ThrowIfCancellationRequested();
      }
      catch (OperationCanceledException)
      {
          progress.Report(new ProgressReport(Level.Warn, "Cancelling Import Operation."));
          throw;
      }
      catch (Exception ex)
      {
          // some stuff then throw Exception to bubble-up
          throw;
      }
  }
  </code></pre>
  
  <p>and </p>
  
  <pre><code>private async Task Unzip(CancellationToken token, IProgress&lt;ProgressReport&gt; progress)
  {
      try
      {
          token.ThrowIfCancellationRequested();
  
          var parent = Path.GetFullPath(Path.Combine(Paths.DataDirectory, ".."));
  
          try
          {
              progress.Report(new ProgressReport(
                  Level.Info, string.Format("Uncompressing export file {0}.", InputFileName)));
  
              using (var zipArchive = ZipFile.Open(InputFileName, ZipArchiveMode.Read, Encoding.UTF8))
              {
                  var extractedCount = 0;
                  var totalCount = zipArchive.Entries.Count;
                  foreach (var entry in zipArchive.Entries)
                  {
                      progress.Report(new ProgressReport(
                          Level.Off, string.Format("Extracting {0}, {1}/{2}",
                          entry.FullName, extractedCount + 1, totalCount), extractedCount, totalCount));
  
                      if (string.IsNullOrEmpty(entry.Name) &amp;&amp; string.IsNullOrEmpty(entry.FullName))
                          continue;
  
                      if (string.IsNullOrEmpty(entry.Name))
                      {
                          var dir = Path.Combine(parent, entry.FullName);
                          if (!Directory.Exists(dir))
                              Directory.CreateDirectory(dir);
                      }
                      else entry.ExtractToFile(Path.Combine(parent, entry.FullName), true);
  
                      notPaused.WaitOne();
                      token.ThrowIfCancellationRequested();
  
                      extractedCount++;
                  }
  
                  // Everything completed, update the progress bar.
                  progress.Report(new ProgressReport(totalCount, totalCount));
              }
          }
          catch (OperationCanceledException)
          {
              progress.Report(new ProgressReport(Level.Warn, "Cancelling Import Operation."));
              throw;
          }
          catch (Exception ex)
          {
              // Some stuff then throw Exception to bubble-up
              throw;
          }
      }
  }
  </code></pre>
  
  <p>At this point, the asynchronous job is working fine, the UI is not frozen but the issue is that <code>Exception</code> thrown in <code>Backup</code>and <code>Unzip</code> methods never bubble-up and aren't catched in <code>Import</code> method, thus the program crash at the <code>throw</code> instruction.</p>
  
  <p>After doing some research, I found in <a href="https://msdn.microsoft.com/en-us/magazine/jj991977.aspx" rel="nofollow">this msdn article</a> that this is a normal behavior when using <code>void</code> returning methods. So I changed the program a bit and now the call with <code>await</code> is made like this :</p>
  
  <pre><code>try
  {
      await Backup(tokenSource.Token, progress);
      await Unzip(tokenSource.Token, progress);
  }
  </code></pre>
  
  <p>And my methods defined like this :</p>
  
  <pre><code>private async Task Backup(CancellationToken token, IProgress&lt;ProgressReport&gt; progress)
  {
      // Same logic
      Task.Delay(1000);
  }
  
  private async Task Unzip(CancellationToken token, IProgress&lt;ProgressReport&gt; progress)
  {
      // Same logic
      Task.Delay(1000);
  }
  </code></pre>
  
  <p>Now exceptions bubble-up well to <code>Import</code> method, but the UI is frozen for the whole job completion time, like the job was processed by the UI thread. Any hint on what's wrong ?</p>