0

I am pretty new in JavaScript technoogies and RxJS and I have the following doubt about how exactly works this code. It is taken from a project on which I am working and it works fine but I need to understand how exactly Promise.all() works and how it is used in this code.

So basically I have this method used to save multiple files on Firebase Store:

public async saveAsset(parameters: any, filesList: any[]) {
    console.log("saveAsset() START, parameters: ", parameters);

    if(filesList != null) {
      //return await this.uploadFileIntoFirebaseStore(filesList);

      let files: any[] = [];
      if (filesList) {                            // If exist files that have to be updated

        /**
         * Retrieve files URLs list on Firebase Storage.
         * uploadFileIntoFirebaseStore() receive the files list to be uploaded and return a Promise resolving 
         * an array of string representing the URLs where of the uploaded files.
         * 
         */
        files = (await Promise.all(this.uploadFileIntoFirebaseStore(filesList)))
          .map(url => {
            console.log("UPLOADED URL: ", url);
            return {
              url
            };
          });
      }

    }
    else {
      //return await this.firestore.collection('assets').doc().set(parameters);
    }

}

Basically my saveAsset() method receive the filesList parameter that is a list of files uploaded by the user that have to be saved on Firebase Storage. After that a file is saved I have to store the files URL.

In order to save these files it is used the uploadFileIntoFirebaseStore() method returning a Promise[]. So it is returning an array of n Promise objects.

So if I am well understanding the Promise concept (correct me if I am doing wrong assertion) it is my uploadFileIntoFirebaseStore() method is returning an object of type Promise that can basically do two different things:

  1. It can be resolved into an array of string (where each string represents the URL of an uploaded file on Firebase Storage).
  2. Be rejected in case an error occurs (I think that it could happens if a file upload fail).

So from what I have understand about the use of Promise.all() method is that it does something after that all the promises of my array are resolved. So in my code I have:

await Promise.all(this.uploadFileIntoFirebaseStore(filesList))

if I well understand the concept it means that my uploadFileIntoFirebaseStore perform the effective upload of my files in Firebase Storage and return an array of Promise where each promises can be resolve in the upload URL. This behavior is asyncronous because a file can be uploaded after or before other files. And here the first doubt: my array of Promise is immediatly returned and then the promises starts to be resolved in an asyncronous way or a Promise object is emitted when the upload of the related file start (something like an Observable). I think that the behavior is the first one but I am not sure.

The second doubt is related to the await keyword in front of the Promise.all() method. It means that this is an async method? From what I can understand the await keyword can be used only with async method.

The last doubt is: looking on various tutorial and documentation I found that often something like this:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});

Basically I have n Promise obbject that are passed as array to the Promise.all() method as array and then I have the then() method executing the arrow function when all the promises objects of the array are resolved (I can also have the catch() method to do something in case of rejection).

This is pretty different from my code where I have not the then() method that do something when all my promises are resolved. Instead my code use the RxJS map() operator on the list of the (resolved?) promises.

From what I can understand this map() operator iterates on the returned resolved promise containing the url of the related file in order to print it in the console and return it as Observable (it seems to me that map() always return an Observable, is it correct?)

How exactly works this syntax? Why is it not using the then()?

AndreaNobili
  • 40,955
  • 107
  • 324
  • 596
  • 1
    *"It means that this is an async method?"* - yes, look at the first line: `public async saveAsset`. You could use `.then` instead, they're interoperable; `async`/`await` is just syntactic sugar on promises. `(await Promise.all(arrayOfPromises))` resolves to an array; `map` isn't the RxJS function, it's the array method. – jonrsharpe Jan 05 '21 at 10:39
  • 1
    I'd suggest reading e.g. https://stackoverflow.com/q/34401389/3001761 and the various resources linked from that if you're not clear on the interaction between `async`/`await` syntax and promises. – jonrsharpe Jan 05 '21 at 10:41
  • `async` & `await` is just syntactic sugar for `promise` & `then`. They're really the same thing. An async function returns a promise and await puts the rest of the function on the event loop and passes it the retrieved value once it's done (Exactly how `.then(()=>{})` works). – Mrk Sef Jan 05 '21 at 14:27

2 Answers2

1

Promise.all

It will take an array of promises and execute them in parallel. It follows the mechanism of all or none

Promise.all([ ...]).then(console.log) will execute if all promises are fulfilled, otherwise it will execute catch Promise.all([ ...]).catch(console.log)

There is one more method Promise.allSettled This will consider a success even one of the promises is failed. return data contains all results with statues

Syntax:-

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.all(promises)
  .then((results) => results.forEach((result) => console.log(result)))
  .catch(console.error);
1

This is a large question, apologies if I don't answer all aspects. Carefully look at the number of brackets:

files = (await Promise.all(this.uploadFileIntoFirebaseStore(filesList)))

Here, the syntax of await (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) will await for the Promises passed to Promise.all.

This mean, the bracket above will eventually evaluate to an array (not a promise).

With an array of strings, map can be called, as this is now just an array of strings.

If you wanted to use then you could do something as such:

Promise.all(...).then(urls => urls.map(...))

I hope this answers the fundamental aspect of your question.

Harley Thomas
  • 414
  • 3
  • 7
  • Yes, now it is pretty clear. The only thing that I can't understand if my Promise.all() method is immediatly receiving the array of all the Promise objects and than wait for the resolution of all the promises (so the asyncronous behavior means that different promises can be resolved at differnt times) or if the Promise objects "arrivea" at different time like an Observable behavior – AndreaNobili Jan 05 '21 at 10:53
  • 1
    @AndreaNobili promises only resolve once, unlike observables - `Promise.all` rejects when the *first* promise rejects, or resolves when *all* have resolved. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all. – jonrsharpe Jan 05 '21 at 11:26