0

I am trying to create a function that handles file uploads in bulk. I have what I call a floor, and each floor has rooms. Every room can have an audio file. This function should handle the upload of these files. My problem is that the updated "floor" gets written before the files have been uploaded. Meaning I can't make it work asynchronously properly.

This is my function: - tracks is an object with the keys being the index of the room and the values the files themselves (using an object and not an array because this is an updating funtction so I might not have all the rooms in this object).

export const saveFloor = (floor, tracks, cb) => async () => {
  const newFloor = { ...floor };

  if (tracks) {
    await Object.keys(tracks).forEach(async (trackKey) => {
      const track = tracks[trackKey];

      const ref = storage.ref(
        `/audio/floor_tracks/${floor.id}/${trackKey}/${track.name}`
      );

      const upload = await ref.put(track);
      if (!upload) return;

      const downloadUrl = await ref.getDownloadURL();
      if (!downloadUrl) return;

      newFloor.rooms[trackKey].track = { name: track.name, file: downloadUrl };
      console.log("this is the floor", "Just finished one track");
    });
    console.log("this is the floor", "Just finished all tracks");
  }
  console.log("this is the floor", newFloor.rooms);

  db.collection("floors")
    .doc(floor.id)
    .set(newFloor)
    .then(() => {
      cb();
    })
    .catch((e) => {
      console.log("Saving to db failed", e);
    });
};

I get Just finished all tracks and newFloor.rooms printed way before Just finished one track.

Edit after the answer from RikkusRukkus, this is my current function, and I am getting the error

Unhandled Rejection (TypeError): undefined is not iterable (cannot read property Symbol(Symbol.iterator))

export const saveFloor = (floor, logoFile, tracks, cb) => async () => {
  const newFloor = { ...floor };

  if (logoFile) {
    const ref = storage.ref(`/images/floor_logos/${floor.id}`);

    const upload = await ref.put(logoFile);
    if (!upload) return;

    const downloadUrl = await ref.getDownloadURL();
    if (!downloadUrl) return;

    newFloor.logo = downloadUrl;
  }

  if (tracks) {
    await Promise.all(
      Object.keys(tracks).forEach(async (trackKey) => {
        const track = tracks[trackKey];

        const ref = storage.ref(
          `/audio/floor_tracks/${floor.id}/${trackKey}/${track.name}`
        );

        const upload = await ref.put(track);
        if (!upload) return;

        const downloadUrl = await ref.getDownloadURL();
        if (!downloadUrl) return;

        newFloor.rooms[trackKey].track = {
          name: track.name,
          file: downloadUrl,
        };
        console.log("this is the floor", "Just finsihed one track");
      })
    );

    console.log("this is the floor", "Just finsihed all tracks");
  }
  console.log("this is the floor", newFloor.rooms);

  db.collection("floors")
    .doc(floor.id)
    .set(newFloor)
    .then(() => {
      cb();
    })
    .catch((e) => {
      console.log("Saving to db failed", e);
    });
};
Tsabary
  • 3,119
  • 2
  • 24
  • 66

1 Answers1

0

await Object.keys(tracks).forEach(async (trackKey) => {

Array.prototype.forEach returns undefined, this will not throw an error because you can await any kind of value, not just Promises. So what happens is:

  1. Your array is looped over
  2. Async processes are started
  3. undefined is returned by forEach
  4. It's not a promise so it immediately resolves and with almost no delay, the next line is executed ("finished all tracks")
  5. Some time later, the promises from #2 resolve. Nothing is done with their resolved values (there are no values in the return statement, so the promises will resolve to undefined).

You can await an array of promises like so:

await Promise.all( Object.keys(tracks).map(async (trackKey) => { ... }) )

Array.prototype.map does return values, in this case Promises. Promise.all takes an array of Promises & resolves when all resolve or rejects ("throws") when any one rejects.

_

If you were to include return values in your async functions then Promise.all will resolve to an array of these returned values, in order.

RickN
  • 12,537
  • 4
  • 24
  • 28