Question

I am using an ID3 reader script to retrieve data from audio files. The basic usage is:

ID3.loadTags(file,function()
{
  var attr = ID3.getAllTags(file).attribute;
});

where the anonymous function is a callback function. This is just to provide context, however, I'm not at all sure the problem I'm having is specific to that particular script.

Typically, inside the callback function, you can extract the info you need and then use the DOM to set the innerHTML attribute of whatever element to equal the info you extracted.

Sometimes you're extracting a bunch of info and connecting all together in a long string though, and I'm trying to compartmentalize it a bit more so that my calling function will be a little cleaner. What I want to do is this:

function callingFunction()
{
  var file = "whatever.mp3";
  var info = getInfo(file);
}

function calledFunction(file)
{
  var info = {data: 0};

  ID3.loadTags(file, function(passedVar)
  {
    var dataobj = ID3.getAllTags(file);
    passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
  }(info));

  return info;
}

An object with an attribute is created because it's one of the only ways to simulate passing by reference in JS - pass the object into the callback function, assign the appropriate data to the attribute in the object, and then at the end of calledFunction, return the object to callingFunction.

It doesn't work though. Now, in that code above, if I said passedVar.data = "teststring" instead of assigning it data from dataobj, that would work, so the passing of the object into the callback function is working correctly. But if the object is assigned data from the data object that the ID3 function returns, it doesn't work. It comes back undefined, and furthermore, Chrome's JS debugger says that the object the ID3 function returns is null. This is further confirmed when I do this:

function calledFunction(file)
{
  var info = {data: 0};

  ID3.loadTags(file, function(passedVar)
  {
    alert(ID3.getAllTags(file).(any attribute));
  }(info));

  return info;
}

and no alert box comes up at all. But if remove the parameter being passed into the callback function in the code above, and leave everything else the same, that alert box comes up like it's supposed to.

So, to sum up, when I pass a parameter into the callback function, for some reason, another function of the object that's calling the callback function ceases to work correctly.

Is it possible that passing a parameter into the callback function is somehow conflicting with, or overriding, whatever the ID3.loadTags function is passing into the callback function, and that's why the getAllTags function is failing? Because for some reason when a parameter is passed into the callback function, the getAllTags function is no longer getting all of the information it needs to run properly? That's the only explanation I can think of.

If that's the case, is there a way to work around it? And if that's not what's going on, what is going on?

I have figured one solution, but I feel like it's hacky. I basically create a third function that gets called from the callback function(which itself receives no parameters), that takes as a parameter the object that the getAllTags method returns, extracts data from that object, and assigns it to global variables that other functions can access. So, this:

var globalVar;

function calledFunction(file)
{
  //var info = {data: 0};

  ID3.loadTags(file, function()
  {
    thirdFunction(ID3.getAllTags(file));
  });

  //return info;
}

function thirdFunction(dataobj)
{
  globalVar = dataobj.title+etc;
}

But I don't really like that solution, I feel like it goes against the spirit of compartmentalization that I was after in the first place with this.

I'd appreciate any help.

Was it helpful?

Solution

The reason this doesn't work:

function calledFunction(file)
{
  var info = {data: 0};

  ID3.loadTags(file, function(passedVar)
  {
    var dataobj = ID3.getAllTags(file);
    passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
  }  (info));
  // ^^^^^^ --- calls the function immediately

  return info;
}

...is that you're calling your anonymous function and passing the result of that call (undefined) into ID3.loadTags. You're not passing a function into it anymore at all.

But the fundamental problem is that you're trying to use the data object before loadTags calls its callback and puts the data on the object.

I suggest that, since its output depends on an asynchronous operation, rather than relying on function return values, you change calledFunction to take a callback function. Here's what it should look like:

function callingFunction() {
  getInfo('whatever.mp3', function(info) { // pass a callback function
    // info.data is here now
  });
}

function getInfo(file, cb) { // accept a callback function as the 2nd param
  ID3.loadTags(file, function() {
    var tags = ID3.getAllTags(file);
    // once your async operation is done, call cb and pass back the return value
    cb({
      data: tags.title+etc+tags.album+....(it can get long);
    });
  });
}

This approach avoids problems you were trying to solve by using an object you could pass by reference, and it ensures you only move on once your asynchronous operation (ID3.getAllTags) is complete.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top