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?

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
double-happiness
  • 137
  • 1
  • 12
  • 1
    `getRelease` is async. It returns a Promise, not a value. You can't simply do `idFiltered.forEach( getRelease )` – Jeremy Thille Nov 03 '21 at 12:57
  • 1
    https://stackoverflow.com/a/37576787/5008997 that will solve your problem :) – d0n.key Nov 03 '21 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? – double-happiness Nov 03 '21 at 13:13

1 Answers1

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);

    });
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63
  • 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. – double-happiness Nov 03 '21 at 13:25
  • 1
    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. – Jeremy Thille Nov 03 '21 at 13:31
  • 1
    Also I'm not sure why you filter an array of numbers by `v !== ''`? – Jeremy Thille Nov 03 '21 at 13:33
  • 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. – double-happiness Nov 03 '21 at 13:42
  • What are `the release IDs`? Which variable, which line? – Jeremy Thille Nov 03 '21 at 13:47
  • Well the variable in question will be `id`, but I don't know which line the error is occurring on, because there is no error message. It's just showing `https://api.*****.com/releases/0` where it should be showing `https://api.*****.com/releases/1' and so on. – double-happiness Nov 03 '21 at 13:51
  • But what's the output of `console.log(id2)`? (By the way I would really use more explicit variable names, like instead of `id` I'd use `fileToLines` or something and then `const lines = fileToLines(file)`. So one can understand what the code does and what it produces) – Jeremy Thille Nov 03 '21 at 14:01
  • The output of `console.log(id2)` is 'Array(11) [ "1", "", "2", "", "3", "", "4", "", "5", "", … ] ​ 0: "1" ​ 1: "" ​ 2: "2" ​ 3: "" ​ 4: "3" ​ 5: "" ​ 6: "4" ​ 7: "" ​ 8: "5" ​ 9: "" ​ 10: "" ​ length: 11 ​ : Array [] csv.js:60:17' I will definitely use better variable names once I work out better what everything does. My code is kind of cobbled together from various suggestions and I'm struggling to properly get a grip of it TBH. – double-happiness Nov 03 '21 at 14:09
  • I tried to remove the `idFiltered = id.filter(function(v){return v!==''});` and just use `id` as the variable throughout, but that just broke my program and I got a whole bunch errors, so reverted that change. There must be some reason why it's needed but IDK what it is. – double-happiness Nov 03 '21 at 14:34
  • OK now the filter makes sense, but it must be put before the conversion to numbers. Answer updated. Also, I have tried something different with `Promise.all` – Jeremy Thille Nov 03 '21 at 14:36
  • Also `map(Number)` is useless; You can perfectly pass `"3"` instead of `3`, it will be appended to a URL anyway – Jeremy Thille Nov 03 '21 at 14:37
  • Thanks, but that's still losing all the data that I had in the console before. When I console log rows I just get a bunch of empty arrays. – double-happiness Nov 03 '21 at 14:50
  • That's very strange. Could you please make a reproductible example online, for instance on [codepen](https://codepen.io/) ? Because really, I don't know what's wrong, but I don't have the full picture to troubleshoot it – Jeremy Thille Nov 03 '21 at 15:15
  • How about this? https://codepen.io/double-happiness/pen/MWvrErX Thanks. – double-happiness Nov 03 '21 at 15:36
  • Aaaaah stupid me, I forgot to `await response.json`, silly mistake. I have forked and updated your Codepen : https://codepen.io/jeremythille/pen/OJjzQdg – Jeremy Thille Nov 03 '21 at 17:31
  • Thanks, I'll try that out soon. – double-happiness Nov 04 '21 at 15:12
  • I just tried that out, and although it doesn't return data for all of the release IDs in the upload file, it did return one, so that's a real step forward! Thanks! :) – double-happiness Nov 05 '21 at 10:10
  • I tried the codepen with a CSV file I've made, with one number per row, and I got all the IDs. I don't know what yours looks like – Jeremy Thille Nov 05 '21 at 14:11
  • That's odd, because I tried it again, and only got one line of output back. Could be a browser issue maybe? I'm on FF 94.0.1. I did manage to come up with a working version of my app. though; I could let you see it if you like. – double-happiness Nov 06 '21 at 16:08
  • Sure, you can update your Codepen, I'll gladly have a look – Jeremy Thille Nov 07 '21 at 19:54
  • Done. (I'm currently working on the `delimiter` aspect, so that is not working properly yet). cheers – double-happiness Nov 08 '21 at 10:59