Sometimes when the function runs it works perfectly, other times it cannot access the properties of the object

52 Views Asked by At
const tj = require('@mapbox/togeojson');
const fs = require('fs');

const gpxToJson = async function (trailSlug) {
  // node doesn't have xml parsing or a dom. use xmldom
  DOMParser = require('xmldom').DOMParser;

  fs.readdir(`./public/traildata/${trailSlug}/gpxFiles/`, (err, files) => {
    let newGpx;
    files.forEach(async (item, i) => {
      const promise = new Promise((res, rej) => {
        fs.readFile(
          `./public/traildata/${trailSlug}/gpxFiles/${item}`,
          'utf8',
          async function (err, data) {
            const gpx = await new DOMParser().parseFromString(data);
            const converted = await tj.gpx(gpx);
            console.log(typeof converted);
            converted.features[0].properties.name = item
              .replace('-', ' ')
              .split('.')[0];

            if (i === 0) {
              newGpx = converted;
            }

            if (i > 0) {
              newGpx.features.push(converted.features[0]);
            }

            if (i === files.length - 1) {
              //   console.log(newGpx);
              fs.writeFile(
                `./public/traildata/${trailSlug}/mastergeoJSON`,
                JSON.stringify(newGpx),
                'utf-8',
                (err) => {
                  if (err) throw err;
                  console.log('The file has been saved');
                }
              );
            }
            res(converted);
          }
        );
      });
      await promise;
    });
  });
};

gpxToJson('terra-cotta');

Hey there, I am trying to make a program that converts GPX files into one GeoJSON File. Everything seems to be going well so far except for every one in 5 times I run the code, I get a TypeError: Cannot read properties of undefined error from "newGpx.features.push(converted.features[0])"

I know the file is an object (as i have done the console.log(typeof converted) and it comes back as an object), I tried promisifying the entire forEach loop (but may have had an issue with the implementation).

Console Screenshot

Does anyone have a resource that might steer me in the right direction?

Thanks in advance.

1

There are 1 best solutions below

1
jfriend00 On BEST ANSWER

Here's a much simpler version that uses a for loop which is promise-aware (a .forEach() loop is not promise-aware) and uses the built-in promise versions of readdir(), readFile() and writeFile() which, when combined with await makes the code a lot simpler.

const tj = require('@mapbox/togeojson');
const fsp = require('fs').promises;
const DOMParser = require('xmldom').DOMParser;

const gpxToJson = async function (trailSlug) {
    const srcDir = `./public/traildata/${trailSlug}/gpxFiles/`;
    const files = await fsp.readdir(srcDir);

    let newGpx;
    for (let file of files) {
        const fullPath = path.join(srcDir, file);
        const fileData = await fsp.readFile(fullPath, {encoding: 'utf8'});
        const gpx = new DOMParser().parseFromString(fileData);
        const converted = await tj.gpx(gpx);
        converted.features[0].properties.name = file.replace('-', ' ').split('.')[0];
        if (!newGpx) {
            newGpx = converted;
        } else {
            newGpx.features.push(converted.features[0]);
        }
    }
    await fsp.writeFile(`./public/traildata/${trailSlug}/mastergeoJSON`, JSON.stringify(newGPx), { encoding: 'utf8' });
};

gpxToJson('terra-cotta').catch(err => {
    console.log(err);
});