0

Here is my code:

var idFiltered = "";
var artists = [];
var country = "";
var released = "";
var genres = [];
var styles = [];
var tracklist = [];

var rows = [
  [idFiltered, artists, country, released, genres, styles, tracklist],
];

var csvContent = [];

function id(file) {
  return new Promise((resolve, reject) => {
    reader = new FileReader();
    reader.onload = function(e) {
      parsedLines = e.target.result.split(/\r|\n|\r\n/);
      resolve(parsedLines);
    };
    reader.readAsText(file);
  });
}

document.getElementById('fileInput').addEventListener('change', function(e) {
  var file = e.target.files[0];

  if (file != undefined) {
    id(file).then(id => {
      console.log(id)
      console.log(parsedLines)
      console.log(typeof id);

      var idInt = id.map(Number);
      idFiltered = id.filter(function(v){return v!==''});

      console.log(idFiltered)

      idFiltered.forEach(idFiltered => {
        getRelease(idFiltered);
      });
      download(csvContent);
    });
  }
});

function getRelease(idFiltered) {
  return fetch(`https://api.*******.com/releases/${idFiltered}`, {
    'User-Agent': 'Dispodger/0.1',
  })
  .then(response => response.json())
  .then(data => {
    if (data.message === 'Release not found.') {
      return { error: `Release with ID ${idFiltered} does not exist` };
    } else {
      const id = data.id;
      artists = data.artists ? data.artists.map(artist => artist.name) : [];
      country = data.country || 'Unknown';
      released = data.released_formatted || 'Unknown';
      genres = data.genres || [];
      styles = data.styles || [];
      tracklist = data.tracklist ? data.tracklist.map(track => track.title) : [];

      console.log(idFiltered);
      console.log(artists, country, released, genres, styles, tracklist)

      rows = [
        [idFiltered, artists, country, released, genres, styles, tracklist],
      ];

      console.log(rows);

    }
  });
}

function download() {
  const ROW_NAMES = ["Release ID", "artists", "country", "released", "genres", "styles", "tracklist"];
  csvContent = "data:text/csv;charset=utf-8,"
  + ROW_NAMES + "\n" + rows.map(e => e.join(",")).join("\n");

  console.log(csvContent);

  var encodedUri = encodeURI(csvContent);
  var link = document.createElement("a");
  link.setAttribute("href", encodedUri);
  link.setAttribute("download", "my_data.csv");
  document.body.appendChild(link); // Required for FF
  link.click();
}

When I console.log rows on line 72, the data is clearly there, and it seems like I'm grabbing the data successfully but just throwing it away; it obviously never makes it into the download function. I originally had the download function as part of GetRelease(), but that wasn't working well, and I was getting one file per line, although with the data I wanted. Now I just get one file, but the data is absent, only the headers are included. Any help please? TIA.

Edit: I am starting to wonder if I'm perhaps returning the wrong thing from getRelease? Maybe instead of return fetch I need to do return rows?

3
  • 1
    getRelease is async. It returns a Promise, not a value. You can't simply do idFiltered.forEach( getRelease ) Commented Nov 3, 2021 at 12:57
  • 1
    stackoverflow.com/a/37576787/5008997 that will solve your problem :) Commented Nov 3, 2021 at 13:03
  • Thanks for the comments so far. @d0n.key - I think I get the general idea, but I'm not really sure what to do with that. Would that replace my entire id function, or just the forEach loop? I'm guessing the latter, but I can't write a function within another, can I? Commented Nov 3, 2021 at 13:13

1 Answer 1

1

getRelease is async. It returns a Promise, not a value. You can't simply do idFiltered.forEach( getRelease ).

Solution : rewrite getRelease with the async/await syntax

async function getRelease(idFiltered) { // Returns a Promise

    const response = await fetch(...);

    const data = await response.json();

    if (data.message === 'Release not found.') {
        return { error: `Release with ID ${idFiltered} does not exist` };
    } else {
        // ....
        console.log(rows);
        return rows;
    }
}

Now you can do this :

for( let id of idFiltered){
    await getRelease(id);
}

This will make every fetch call in order, one after the other. Alternatively, you can make them all in parallel with Promise.all :

await Promise.all( idFiltered.map(getRelease) );

Then you can download(csvContent);.

document.getElementById('fileInput')
    .addEventListener('change', async function (e) { // add "async"

        const file = e.target.files[0];

        if (!file) return;

        const id2 = await id(file);
        
        console.log(id2)
        console.log(parsedLines)
        console.log(typeof id2);

        const idFiltered = id2.filter(v => v !== '');

        console.log(idFiltered)

        await Promise.all(idFiltered.map(id => getRelease(id) ));

        download(csvContent);

    });
Sign up to request clarification or add additional context in comments.

21 Comments

thanks, I'm trying that, but I'm getting "await is only valid in async functions, async generators and modules" for line 44. I tried making the id function async but that didn't seem to fix it. This is helpful to learn about, in any case.
Yes you need to add async to the functions that use await. async functions always return a Promise, not a value. I have updated my answer, rewrote your click handler. I noticed that you use the name id for several things. Don't do that, it gets confusing really quickly. I have used id2, rename it as you want.
Also I'm not sure why you filter an array of numbers by v !== ''?
I tried that, but the release IDs are all 0, so it's not working and not retrieving any data at all now. I'm not really sure about the filter either TBH.
What are the release IDs? Which variable, which line?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.