0

I'm trying to retrieve the download urls for 2 images from Firebase, in order to pass on to another method as strings to download them.

For a single image, I'm using this code :

img1_ref.GetDownloadUrlAsync().ContinueWith((Task<Uri> task) => {
  if (!task.IsFaulted && !task.IsCanceled) {
  
    UnityMainThreadDispatcher.Instance().Enqueue(() => UIcontroller.displayImage(task.Result.ToString()));
  

  }
});

Which works perfectly - but I can't figure out how to fetch two urls, make sure I've got them all, then pass them onto the download method... I'm struggling to get my head around aSync...

Any help grateful received!

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

2 Answers2

1

If you want to use Continuations, it's relatively straightforward.

First I'd suggest changing your original code to this:

img1_ref.GetDownloadUrlAsync().ContinueWithOnMainThread((Task<Uri> task) => {
  if (!task.IsFaulted && !task.IsCanceled) {
    UIcontroller.displayImage(task.Result.ToString()));
  }
});

This uses a Firebase extension to task to move your logic onto the main thread in a more concise manner.

Next, you can use Task.WhenAll to create a task that waits for multiple tasks to complete:

var task1 = img1_ref.GetDownloadUrlAsync();
var task2 = img2_ref.GetDownloadUrlAsync();

Task.WhenAll(task1, task2).ContinueWithOnMainThread((Task<Uri>[] tasks) => {
  if (!task.IsFaulted && !task.IsCanceled) {
    // work with tasks here. You can also use IEnumerable<Task<Uri>>
  }
});

Of course, now we can have a little fun. I'd recommend reading this article on threading in Firebase for a little more background.

You can use @Frank van Puffelen's answer and do something like this:

async void DownloadAsync() {
  var task1 = img1_ref.GetDownloadUrlAsync();
  var task2 = img2_ref.GetDownloadUrlAsync();

  await Task.WhenAll(task1, task2);
  // you're on the calling thread here, which is usually the main thread
  UIcontroller.displayImage(task1.Result.ToString());
}

Or you can move this all to a coroutine to get a little bit more Unity-aware memory safety:

IEnumerator DownloadCoroutine() {
  var task1 = img1_ref.GetDownloadUrlAsync();
  var task2 = img2_ref.GetDownloadUrlAsync();

  yield return new WaitUntil(()=>Task.WhenAll(task1, task2).IsComplete);

  // coroutines always run on the main thread, and only if this hasn't been destroyed
  UIcontroller.displayImage(task1.Result.ToString());
}
Patrick Martin
  • 2,993
  • 1
  • 13
  • 11
  • Fantastically helpful, thanks both! I have that up and running perfectly now :) So, the next step of this, looking at the SendWebRequest unity method, I'm right in thinking that that *can't* be run async, right? So, I can use a similar solution to download and then render the textures (sadly...)? – Iain Simons Sep 21 '20 at 18:38
  • UnityWebRequest _can_ be used in a coroutine. So use my last example – Patrick Martin Sep 21 '20 at 20:33
0

Based on Running multiple async tasks and waiting for them all to complete that'd be:

var task1 = img1_ref.GetDownloadUrlAsync();
var task2 = img2_ref.GetDownloadUrlAsync();

await Task.WhenAll(task1, task2);

...
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Note that you can `ContinueWith` or `ContinueWithOnMainThread` the result of `WhenAll` instead of just `await`ing it. So your code could look entirely unchanged. Firebase also already provides a way to dispatch to the main thread with `ContinueWithOnMainThread`, so you can get rid of the `UnityMainThreadDispatcher` if you'd like (https://firebase.google.com/docs/reference/unity/class/firebase/extensions/task-extension). – Patrick Martin Sep 21 '20 at 15:22
  • Sounds like the start of a great answer Patrick. ;-) – Frank van Puffelen Sep 21 '20 at 15:35