Search code examples
javascriptnode.jsasynchronousioknex.js

Knex promises inside for loop


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,
...

Solution

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