0

I'm new to Node/asynchronous coding and I realize this is a basic question that speaks to some fundamentals I'm missing, but for the life of me I just can't understand how this is supposed to work. I'm seeding a database using knex, by reading in data from a CSV and iterating through the rows in a for loop. Within that loop, I need to query another table in the database and return an associated value as part of the new row to be created.

I understand that knex returns a pending Promise, so it doesn't have access to the returned value yet. But I can't seem to get the structure right with async/await or Promise.all; I've tried it a bunch of ways based on other answers I've seen, but none seem to actually explain what's happening well enough that I can apply it successfully to my case. Any help hugely appreciated.

My seed file currently looks like this:

exports.seed = function(knex) {
  const fs = require('fs');

  function createImage(row, event_id) {
    return {
      name: row[1],
      event_id: event_id
    }
  };

  function get_event_id(loc) {
    knex('events')
      .where({location: loc})
      .first()
      .then(result => {console.log(result)});    // <-- this never executes
  };

    let images = [];

    const file = fs.readFileSync('./data.csv');
    const lines = file.toString().replace(/[\r]/g, '').split('\n');

    for (i=1; i<lines.length; i++) {
      var row = lines[i].split(',');

      var event_id = (async function () { return await get_event_id(row[0]) }) ();

      images.push(createImage(row, event_id));

      };

    console.log(images);
    // Inserts seed entries
    // return knex('table_name').insert(images);

};

Output: [ { name: '003.jpg', event_id: undefined } ]

My data.csv is structured like:

Location,Name
Amsterdam,003.jpg,
...
user6647072
  • 131
  • 2
  • 17
  • Does this answer your question? [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – AZ_ Jan 29 '20 at 07:12
  • @AZ_ If it does, you might have to spell it out for me a bit more (sorry). I think what I'm missing has to do with `Promise`s rather than closure per se. For example, running ```lines.slice(0,2).forEach(function(line) { knex('events') .where({location: line[0].split(',')[0]}) .first() .then(result => {console.log(result)}); })``` doesn't seem to help (that console.log still doesn't execute). But maybe I'm missing your point. – user6647072 Jan 29 '20 at 07:23

1 Answers1

1

You can change your for loop into an Array.map and use Promise.all on returned promises.

Also, seed will return a promise so invoke it properly

exports.seed = async function (knex) {
    const fs = require('fs');

    function createImage(row, event_id) {
        return {
            name: row[1],
            event_id: event_id
        }
    };

    function get_event_id(loc) {
        return knex('events')
            .where({ location: loc })
            .first()
            .then(result => { console.log(result); return result });    // <-- this never executes
    };


    const file = fs.readFileSync('./data.csv');
    const lines = file.toString().replace(/[\r]/g, '').split('\n');

    let promises = lines.map(async (line) => {
        let [row] = line.split(',');
        let event_id = await get_event_id(row)
        return createImage(row, event_id)
    });

    let images = await Promise.all(promises);

    console.log(images);
    // Inserts seed entries
    // return knex('table_name').insert(images);
}; 
AZ_
  • 3,094
  • 1
  • 9
  • 19
  • That seems (ahem) promising, but I can't get `lines.map()` to work -- it says it's not a function, despite the fact that `Array.isArray(lines)` returns true. Any ideas? – user6647072 Jan 29 '20 at 08:27
  • `Error: Error while executing `_filename_ `seed: lines.map(...) is not a function at Promise.resolve.then.then.catch `_path_`/node_modules/knex/lib/seed/Seeder.js:146:25) TypeError: lines.map(...) is not a function at Object.exports.seed (`_path_`/db/seeds/addMimo.js:37:7) at Promise.resolve.then (`_path_`/node_modules/knex/lib/seed/Seeder.js:143:26)` – user6647072 Jan 29 '20 at 08:33
  • 1
    my bad, fixed typo. – AZ_ Jan 29 '20 at 08:35
  • Hmm, it runs now, but the `images` array that prints at the end shows all undefined values. – user6647072 Jan 29 '20 at 08:41
  • updated iteration to call `createImage` also, and check if you are getting `event_id`. – AZ_ Jan 29 '20 at 08:45
  • Yeah, it's still not getting the `event_id`; whatever comes after the `await get_event_id` doesn't seem to be properly awaiting it. Does it actually work for you? – user6647072 Jan 29 '20 at 08:57
  • ahh there is no return in `get_event_id`, should be `return knex('events')` – AZ_ Jan 29 '20 at 09:19
  • That works! Now I will have to study this example to understand why... the double `async/await` is a bit confusing to me still. – user6647072 Jan 29 '20 at 18:28
  • double async-await? every function needs to be `async` if you want to use `await` inside. – AZ_ Jan 30 '20 at 06:51