Vikaton
Vikaton

Reputation: 2407

Why am I getting a Segmentation fault in Node.js?

I have this function right here:

function createInsta(email, userid) {
  if (!fs.existsSync('users/' + email + '/' + userid)) {
    fs.mkdir('users/' + email + '/' + userid)
    fs.writeFileSync(('users/' + email + '/' + userid +  '/instagram.json'), fs.readFileSync("data/data.json", "utf-8"))
    console.log("insta folder created");
  }
  console.log("Initializing Data")
  var data = fs.readFileSync('users/' + email + '/' + userid +  '/instagram.json', "utf-8")
  data = JSON.parse(data);
  var result;
  request(("https://instagram.com/" + userid + "/?__a=1"), function(error, response, body) {
    var res = JSON.parse(body);
    result = res.user.followed_by.count
    if (error) {
      console.log(err)
    }
  })
  while (result === undefined) {
    deasync.sleep(100)
  }
  data.STARTING_COUNTS[0].DAY = result;
  data.STARTING_COUNTS[0].WEEK = result;
  data.STARTING_COUNTS[0].MONTH = result;

}

right after the console prints outs Initializing Data, the console says Segmentation fault

I have no clue why this is, my code does not seem like anything could cause such an error.

Any pointers? Thanks

Upvotes: 8

Views: 32835

Answers (2)

Keith
Keith

Reputation: 24211

Ok, lets refactor our last post.

There was only one reason we use the new Promise() constructor, and that was because the request is Async. In fact I made a slight mistake really as some errors won't be handled in the correct way. What I should have done is move the new Promise() to just before the request.

Now one great thing about Promises is that error's will propagate down the chain, in our example so far we have a console.log() inside our request object. What would be nice is that all un-handled errors are handled in one place. In this example the run() function seems a nice place.

If you now look at your createInsta function you would think it's using all sync function, but in fact apart from existsSync everything else is async. Like mentioned in the comments, existSync really wants removing anyway,. Instead just create the directory and catch the error in a try catch, or even better make a utility function say called tryMkDir, that does this (promise based of course).. :)

One last note, await / async is a pretty new javascript, I think it's ES7, and I don't believe it's yet even finalised. But you can use it now, by using something like Babel.

//lets make a Promise based request.
//There is an NPM module that would do this and more
//but this is a good example of making existing callback
//functions into a Promise.

async function promRequest(url) {
  return new Promise(function(resolve, reject) {
    request(url, function(error, response, body) {
      if (error) return reject(error);
      //promises can only return one thing
      //so use an object literal to return more
      resolve({
        response:response,
        body: body
      });
    });
  });
}

//lets also promisify some Node stuff.
var Promise = require('bluebird');

//there is a promisifyall, but I like to be
//specific about what I promisify.
var fsWriteFile = Promise.promisify(fs.writeFile),
    fsReadFile = Promise.promisify(fs.readFile),
    fsMkdir = Promise.promisify(fs.mkdir);


//because promRequest is already a promise
//we can now get rid of the new Promise contructor here.
//The only part of this function that is now async
//is existsSync, the exists() in node is depreciated
//due to potential race conditions, really this 
//should be altered to just try and create dirctory
//and catch error if exists.
async function createInsta(email, userid) {
  if (!fs.existsSync('users/' + email + '/' + userid)) {
    await fsMkdir('users/' + email + '/' + userid);
    await fsWriteFile(('users/' + email + '/' + userid +  '/instagram.json'), await fsReadFile("data/data.json", "utf-8"));
    console.log("insta folder created");
  }
  console.log("Initializing Data")
  var data = await fsReadFile('users/' + email + '/' + userid +  '/instagram.json', "utf-8")
  data = JSON.parse(data);    
  var res = JSON.parse(
    await promRequest("https://instagram.com/" + userid + "/?__a=1").body);
  var result = res.user.followed_by.count
  data.STARTING_COUNTS[0].DAY = result;
  data.STARTING_COUNTS[0].WEEK = result;
  data.STARTING_COUNTS[0].MONTH = result;
  return data;
}

//to be able to use await our function also needs to be async
async function run() {
  //lets catch all unahandled errors here.
  try {
    var data = await createInsta('[email protected]');
  } catch (e) {
    //fantasic any error's can now be logged
    //eg. fsMkdir might have failed and it's still
    //going to be logged here..
    console.log(e);
  }
}

Upvotes: 4

Keith
Keith

Reputation: 24211

As promised (pardon the pun), here is getting started into converting your code into Promise / await.

This is kind of a start of making your code into using promises, there is more error checking needed etc, but I've deliberately done as little to the code for now too show changes, and we can refactor as we go on.

eg. all that fs.existsSync etc, want's making into promises, so we can get rid of all the sync stuff. Your node.js App will love you for it.

Later, we can do more. Promise's built into browser/node are fine, but I also find a Promise lib can make things easier, I would suggest bluebird -> http://bluebirdjs.com/docs/getting-started.html It might be worth having a good read there too, bluebird has some useful utility functions like promisify that will make your fs.func's easier.

So I think this should do for now, and we'll do another refactor later.

//lets mark this function as async..
async function createInsta(email, userid) {
  return new Promise(function (resolve, reject) {
    if (!fs.existsSync('users/' + email + '/' + userid)) {
      fs.mkdir('users/' + email + '/' + userid)
      fs.writeFileSync(('users/' + email + '/' + userid +  '/instagram.json'), fs.readFileSync("data/data.json", "utf-8"))
      console.log("insta folder created");
    }
    console.log("Initializing Data")
    var data = fs.readFileSync('users/' + email + '/' + userid +  '/instagram.json', "utf-8")
    data = JSON.parse(data);    
    request(("https://instagram.com/" + userid + "/?__a=1"), function(error, response, body) {
      if (error) {
        console.log(err)
        return reject(error);
      }
      var res = JSON.parse(body);
      var result = res.user.followed_by.count
      data.STARTING_COUNTS[0].DAY = result;
      data.STARTING_COUNTS[0].WEEK = result;
      data.STARTING_COUNTS[0].MONTH = result;
      resolve(data); //i'm assuming data is what your wanting to return
    })
  });
}

//to be able to use await our function also needs to be async
async function run() {
 var data = await createInsta('[email protected]');
}

Upvotes: 6

Related Questions