Samir Said
Samir Said

Reputation: 373

async - callback already used error

I'm getting a 'callback already used' error and I don't know why. I am using async and want to chain two functions because the second function depends on the first function to complete.

I'm new-ish to Node.js and still wrapping my head around async/callbacks. Thanks so much for helping out.

getCdn takes in cnames, and if the cname is part of a CDN it pushes the results into a global variable called cdnAttrs.

function getCdn(cnameDict, callback) {
  // cdnAttributes contains associative array with each web attribute: {name_in_db : code_snippet_to_find_in_cname}
  for (var key in cdnAttributes) {
    if (cdnAttributes.hasOwnProperty(key)) {
      var snippet = -1;
      // some technologies contain multiple code snippets, in that case they are stored as array. Single code snippets are stored as string
      if (!Array.isArray(cdnAttributes[key])) {
        snippet = cnameDict['cname'].indexOf(cdnAttributes[key])
      }
      else {
        // check each code snippet within the array, if any match the source code, update 'snippet'
        for (var n = 0; n < cdnAttributes[key].length; n++) {
          var val = cnameDict['cname'].indexOf(cdnAttributes[key][n])
          if (val > -1) {
            snippet = val
          }
        }
      }
      // if attribute found in tag, create cdnAttrs[cdn] = [{from: hostname, proof: cname}, {from: hostname2, proof: cname2}, ...]
      if (snippet > -1) {
        try {
          cdnAttrs[key].push(cnameDict);
        }
        catch (e) {
          cdnAttrs[key] = [];
          cdnAttrs[key].push(cnameDict);
        }
        callback();
      } else {
        callback();
      }
    } else {
      callback();
    }
  }
}

My async function looks like this:

async.series([
  // THIS FUNCTION WORKS FINE... 
  function(callback) {
    async.each(toCheck, function(hostname, callback) {
      getCname(hostname, callback);
    },callback);
  },
  // THIS FUNCTION RETURNS RETURNS Error("Callback was already called.")
  function(callback) {
    async.each(toCheckCnames, function(cnameDict, callback) {
      getCdn(cnameDict, callback);
    },callback);
  }
], function(err){
  if(err) {
    console.log('ERROR');
  }else{
    console.log('toCheckCnames is done: '+JSON.stringify(toCheckCnames));
    console.log('cdnAttrs is done: '+JSON.stringify(cdnAttrs));
  }
})

the getCnames function works:

function getCname(hostname, callback){
  dns.resolve(hostname, 'CNAME', function(error, cname) {
    if (cname) {
      toCheckCnames.push({from: hostname, cname: cname[0]});
        callback();
      }
    // if not CNAMEd, check SOA on www.domain.com and domain.com
    else {
      if (hostname.slice(0,4) == 'www.') {
        hostname = hostname.slice(4);
      }
      nativedns.resolve(hostname, 'SOA', function(error, records) {
        if(!error && records) {
          toCheckCnames.push({from: hostname, cname: records[0]['primary']});
          callback();
        }
        else if (!error) {
          hostname = 'www.'+ hostname
          nativedns.resolve(hostname, 'SOA', function(error, records) {
            if (!error) {
              toCheckCnames.push({from: hostname, cname: records[0]['primary']});
              callback();
            }
            else callback()
          });
        }
        else callback()
      });
    }
  });
}

Upvotes: 0

Views: 525

Answers (1)

Tim Brown
Tim Brown

Reputation: 3241

Your getCdn function is a loop that will call the callback after each iteration. If calling the callback is intended to stop the loop execution, you can do return callback(). Otherwise you need to reorganize your code to only call the callback once when the function is done.

UPDATE:

You can also simplify your async.each calls:

// Was this
async.each(toCheck, function(hostname, callback) {
  getCname(hostname, callback);
},callback);

// Could be this
async.each(toCheck, getCname, callback);

Upvotes: 1

Related Questions