caolan/highland

Unhandled Rejection: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined

Closed this issue · 4 comments

How to use highlandjs with async await ? Here my code, simple one First i read the csv file, using fast-csv to remove first line and make the and for each line i check it there is a data or not if there is a data its console.log() some message. if not the data saved but. for now if there is a data the data still saved and not console.log() my message. and if every stream is pass it returning same error message

Unhandled Rejection: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined

LIbrary im using :

  • Fast-Csv
  • Bookshelf
  • fs

here the full code :

DescController.CompaniesCsv = async (req, res, next) => {

  const file = fs.createReadStream(path.join(req.files[0].path), 'utf8').pipe(csv({headers : true}))

  // Read data streams from here with highlandjs
  _(file)
      .each(async (companyData) => {
        // console.log(companyData)
        const sectorName = capitalize(companyData.sector);
        const companyName = capitalize(companyData.name);
        const companyCountry = capitalize(companyData.country);

        const getSector = await Sector.get({ name: sectorName });
        const getCompany = await Company.get({ name: companyName });
        const getCountry = await Country.get({ name: companyCountry });

        const [sector, company, country] = await Promise.all([getSector, getCompany, getCountry]);

        let serSector;
        if(!sector) {
          console.log('Please add a sector first on the admin');
        } else {
          serSector = sector.serialize()
        }

        let serCountry;
        if(!country) {
          serCountry = await Country.forge({ name: companyData.name, order: 1 }).save();
        } else {
          serCountry = country.serialize()
        }

        if(company) {
          console.log('There is a company', company.serialize());
        } else {
          const saveCompany = await Company.forge({ name: companyData.name, country_id: serCountry.id , user_id: req.user.id }).save();
          const insertSector = await CompanySector.forge({ company_id: saveCompany.get('id'), sector_id: serSector.id }).save();
          await Promise.all(...insertSector)
        }
      })

  return res.redirect('companies');
}

the full error message :

::ffff:127.0.0.1 - - [08/Dec/2018:08:19:52 +0000] "GET /companies HTTP/1.1" 200 - "http://localhost:4000/companies" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0"
Unhandled Rejection: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
    at Function.all (<anonymous>)
    at _callee2$ (C:\DATA\source\code\build\modules\desc\controller.js:200:44)
    at tryCatch (C:\DATA\source\code\node_modules\regenerator-runtime\runtime.js:65:40)
    at Generator.invoke [as _invoke] (C:\DATA\source\code\node_modules\regenerator-runtime\runtime.js:303:22)
    at Generator.prototype.(anonymous function) [as next] (C:\DATA\source\code\node_modules\regenerator-runtime\runtime.js:117:21)
    at step (C:\DATA\source\code\build\modules\desc\controller.js:46:191)
    at C:\DATA\source\code\build\modules\desc\controller.js:46:361
    at <anonymous>

Solved the problem was on the await Promise.all(...insertSector) I'm still open for feedback on using async await with highlandjs. thanks

vqvu commented

async await works with Promises, so you need to convert highland streams to and from promises to use it. To convert from promises, use the contructor (_(aPromise)). To convert to promises, use toPromise.

In your example, you can do something like this

DescController.CompaniesCsv = async (req, res, next) => {

  const file = fs.createReadStream(path.join(req.files[0].path), 'utf8').pipe(csv({headers : true}))

  // Read data streams from here with highlandjs
  await _(file)
      // Create promises that represent the async computations.
      .map(async (companyData) => {
        ...
      })
      // Convert the promises to streams and wait for them to complete.
      // You can replace flatMap(_) with map(_).merge() or map(_).mergeWithLimit(n) to increase
      // the amount of parallelism.
      .flatMap(_)
      // Throw away all of the results of the promises.
      .filter((ignored) => false)
      // Convert back to a promise so that you can await it.
      // Replace this with resume() if you want to return the redirect before you're done
      // with the async work.
      .toPromise(Promise)

  return res.redirect('companies');
}

@vqvu is this still okay to .each and not .map ?

vqvu commented

Using each like you did originally is OK if you don't want to wait for the async computations to complete before you do the redirect.