Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

kick off background task and return immediately

I have a method called DownloadFileAsync(url, Func<RemoteFileResponse, Task> onDownloadFinished) which performs the following:

  • Checks the cache and if found, returns the cache immediately and starts a background task to see if the cache needs to be updated
  • If not found, returns null and kicks off a background task to download the file asynchronously
  • After the file is downloaded it calls back on the onDownloadFinished handler.

This feels dirty to me as I need to perform the download on a background thread but I can't await it because I want to be able to return the cached file right away. The problem is that I lose any exception context if I do this.

Some options I can think of:

  • Use the IProgress interface to report back the cached file and then when the download is finished report back the downloaded result.
  • Split the method into two calls (one to get the cached file) another to download/update (not a preferred way since I want to keep my interface to one method).

Was wondering if anyone had any other suggestions of how I could do this?

Pseudo code for my method:

Task<IFile> async DownloadFileAsync(url, Func<RemoteFileResponse, Task> onDownloadFinished)
{
   var cache = await CheckCacheAsync(url);

   // don't await this so the callee can use the cached file right away 
   // instead return the download result on the download finished callback
   DownloadUrlAndUpdateCache(url, onDownloadFinished);

   return cache
}
like image 825
Justin Horst Avatar asked Aug 04 '26 19:08

Justin Horst


1 Answers

If your cache is an in-memory cache, then it's easier to cache the tasks rather than their results:

ConcurrentDictionary<Url, Task<IFile>> cache = ...;

Task<IFile> DownloadFileAsync(url) // no async keyword
{
  return cache.GetOrAdd(url, url => DownloadUrlAsync(url));
}

private async Task<IFile> DownloadUrlAsync(url)
{
  ... // actual download
}

Logically, the GetOrAdd is doing this (but in a thread-safe and more efficient manner):

if (cache.ContainsKey(url))
  return cache[url];
cache[url] = DownloadUrlAsync(url);
return cache[url];

Note, however, that this will cache the complete task, so download exceptions are also cached.

like image 108
Stephen Cleary Avatar answered Aug 07 '26 07:08

Stephen Cleary



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!