Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async await or Promise not returning on stream event

Tags:

I have the following function which returns a Promise where the function argument is an async function:

  createObjectFrom(record) {
    const self = this;
    return new Promise(async (resolve, reject) => {
      let obj = {};

      for(let i = 0, l = self.opts.transformers.length; i < l; i++) {
        let transformer = self.opts.transformers[i];

        const headerIndex = findIndex(self.headers, (header) => {
          return header === transformer.column;
        });

        let csvValue = record[headerIndex];

        const lookUp = transformer.options.lookUp;

        const whereClause = {};

        whereClause[lookUp.column] = csvValue;

        console.log('before await');

        const result = await self.knex(lookUp.table).where(whereClause).select(lookUp.scalar);

        console.dir(result);

        obj[transformer.field] = result[0][lookUp.scalar];
      }

      return resolve(obj);
    });
  }

If I call the function like this from at test, it all executes correctly:

  it('creates the transformed object', async () => {
    const csvRecord = ['PREMIER', '07/11/1998', manager, 'Liverpool', 'Wimbledon', 0, 1, 'A', 0, 1, 'A'];

    const record = await transformer.createObjectFrom(csvRecord);

    expect(record.division).to.equal('PREMIER');
  }

But when calling the createObjectFrom function during a readable event which is raised from a stream created from csv-parse:

  onReadable() {
    let record = this.parser.read();

    if (record === null) {
      return;
    }

    if (this.parser.count <= 1) {
      this.headers = record;
    } else {
      const recordPromises = this.createObjectFrom(record);
      this.recordPromises.push( newRecord );
    }
  }

THe code gets to the console.log statement below in createObjectFrom

  console.log('before here');
  const result = await self.knex(lookUp.table).where(whereClause).select(lookUp.scalar);
  console.dir(result);

But does not get to the console.dir statement below as the Promise does not seem to resolve.

If I call createObjectFrom from a test outside of the stream processing then it resolves correctly.

I have also tried refactoring async await out of this to just return a promise but it is still broke.

If I console.dir the promises on the [end][3] event of the stream they look like this:

[ Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined },
  Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined } ]

I have this repo which has the source code and a failing test.

I am baffled as to what is going on.

The following test also passes so it is definitely something to do with the stream:

  it('creates the transformed object', async () => {
    const csvRecords = [
      ['PREMIER', '07/11/1998', manager, 'Liverpool', 'Wimbledon', 0, 1, 'A', 0, 1, 'A'],
      ['PREMIER', '11/11/1998', manager, 'QPR', 'Sunderland',3,3, 'Sunderland',0,0,'Sunderland'],
      ['PREMIER', '14/11/1998', manager, 'Southampton', 'Liverpool', 3, 3, 'D', 0, 0, 'D']
    ];

    for(var i = 0, l = csvRecords.length; i < l; i++) {
      const csvRecord = csvRecords[i];

      const record = await transformer.createObjectFrom(csvRecord);

      expect(record.division).to.equal('PREMIER');

      expect(record.manager_id).to.equal(manager_id);
    }
  }
like image 252
dagda1 Avatar asked Sep 07 '16 05:09

dagda1


2 Answers

why you don't change your code like this:

createObjectFrom: async (record) => {
    const self = this;
    // return new Promise(async (resolve, reject) => {
      let obj = {};

      for(let i = 0, l = self.opts.transformers.length; i < l; i++) {
        let transformer = self.opts.transformers[i];

        const headerIndex = findIndex(self.headers, (header) => {
          return header === transformer.column;
        });

        let csvValue = record[headerIndex];

        const lookUp = transformer.options.lookUp;

        const whereClause = {};

        whereClause[lookUp.column] = csvValue;

        console.log('before await');

        const result = await self.knex(lookUp.table).where(whereClause).select(lookUp.scalar);

        console.dir(result);

        obj[transformer.field] = result[0][lookUp.scalar];
      }
      return obj;
      // return resolve(obj);
    // });
  }
like image 92
chenkehxx Avatar answered Sep 22 '22 16:09

chenkehxx


OMG, what an arse, it was because I was not returning the promise from the test. When testing promises with mocha, you need to return the promise from the it function. If you do this it all works. What a fool!

So if I change the test to this:

  describe('transformer', () => {
    it('transforms the data and imports the csv file', () => {

      const ignoreIf = (data) => data[3] !== 'Liverpool' && data[4] !== 'Liverpool';
      const opts = { table: 'results', file: __dirname + '/fixtures/test.csv', encoding: 'utf8', transformers, ignoreIf: ignoreIf };

      const f = seeder(opts)(knex, Promise);

      return f.then((results) => {
        console.dir(results);
      });
    });

I return f and all is good now.

like image 36
dagda1 Avatar answered Sep 25 '22 16:09

dagda1