title: Async Report File Generation With Ping For File Completion
body: <p>Recently, I have had need of designing an improved reporting system for generation of large reports.</p>
  <p>The issue with the old system was how long the browser would hang on the request waiting for the file to be ready. If it took too long, the request would be timed out.</p>
  <p>To avoid this, and make for a better UI, I have designed the following process:</p>
  <ul>
  <li>User clicks button.</li>
  <li>An ajax request is fired and awaited for the file name.</li>
  <li>An ajax request is fired and not awaited to generate the report (The test case for this is a WebMethod who's Thread sleeps for 5 seconds).</li>
  <li>An ajax ping based off of long polling will ping the server every x number of seconds to check if the file exists.</li>
  </ul>
  <p>I was a bit rusty with Async, so a lot of my work was following by example. However, the final result I thought should work generated the following result:</p>
  <blockquote>
  <p>9:23:51 AM<br/>
  9:23:51 AM<br/>
  before ping<br/>
  ping 9:23:51 AM<br/>
  Your file is not ready for download...<br/>
  null<br/>
  ping 9:23:56 AM<br/>
  Your file is not ready for download...<br/>
  ping 9:23:57 AM<br/>
  Your file is not ready for download...<br/>
  ping 9:23:58 AM<br/>
  Your file is not ready for download...</p>
  </blockquote>
  <p>The file creation would go off async and generate the file. But as soon as something else was awaited (the ping), the entire process would hang until the file was created, and then the ping logic would loop as desired.</p>
  <p>My first attempt at a solution was not awaiting the ping either. However, I would be left with a promise I could not read to get the response of the ping out of.</p>
  <p>The final solution I realized after a nights sleep was the ping call must be calling the same thread currently locked by the file creation. So I wrapped the whole file creation logic in a Task which achieved the result I desired.</p>
  <p>However, whilst I have a solution, I still would like to understand the problem more.</p>
  <p>Thank you for reading.</p>
  <p>Code:</p>
  <pre><code>document.getElementById(&quot;btnGo&quot;).onclick = async function (e) {
      await LaunchReport(btn.attributes[&quot;bespokeReport&quot;].value);
  };
  
  async function LaunchReport(report) {
      let fileName = await AjaxRequest(&quot;GenerateFileName&quot;, { report: report });
      let reportData;
  
      switch (report) {
          case &quot;StandardReport&quot;: {
              console.log(new Date().toLocaleTimeString(&quot;en-US&quot;));
              AjaxRequest(&quot;RunStandardReport&quot;, { fileName: fileName }).then(console.log);
              console.log(new Date().toLocaleTimeString(&quot;en-US&quot;));
  
              break;
          }
  
          default: {
              //error logic
          }
      }
      console.log(&quot;before ping&quot;);
      await PingReport(fileName);
      console.log(&quot;after ping&quot;);
  }
  
  async function PingReport(fileName) {
      let response = await AjaxRequest(&quot;PingReportStatus&quot;, { fileName: fileName });
      console.log(&quot;ping &quot; + new Date().toLocaleTimeString(&quot;en-US&quot;));
      console.log(response);
  
      if (response != &quot;ready&quot;) {
          await new Promise(resolve =&gt; setTimeout(resolve, 1000));
          await PingBespokeReport(fileName);
      } else {
          window.alert(response);
      }
  }
  
  async function AjaxRequest(action, data = null) {
      let r;
  
      await $.ajax({
          type: &quot;POST&quot;,
          contentType: &quot;application/json; charset=utf-8&quot;,
          data: JSON.stringify(data),
          dataType: &quot;json&quot;,
          url: &quot;/admin/reports.aspx/&quot; + action,
          async: true,
          success: function (res) { r = res.d; },
          error: function (res) {
              console.log(res);
              r = res.d;
          }
      });
  
      return r;
  }
  </code></pre>
  <p>The site is a .NET Framework 4.6.1 site.</p>
  <pre><code>&lt;WebMethod&gt;
  Public Shared Sub RunStandardReport(fileName As String)
      Thread.Sleep(5000)
      ''Generate File Logic
  End Sub
  
  &lt;WebMethod&gt;
  Public Shared Function PingReportStatus(fileName As String) As String
      Try
          Dim fileReady As Boolean = File.Exists(&quot;FileDirectory...&quot; &amp; &quot;\&quot; &amp; fileName)
  
          If fileReady Then
              Return &quot;ready&quot;
          Else
              Return &quot;Your file is not ready for download...&quot;
          End If
  
      Catch ex As Exception
          Return &quot;There was a problem looking for your file with the name: &quot; &amp; fileName
      End Try
  End Function
  </code></pre>