1

I am trying to pass a callback function inside a self-invoking function in JavaScript but I get 'undefined' when done function is executed.

I read this answer to write this code below:

function done() {
    console.log(dateFilter.getI());
    console.log(dateFilter.getF());
}

var dateFilter = (function(callback) {
    var _dInicio = new Date(), _d = new Date(),
        _dFim = new Date(_d.setMonth(new Date().getMonth() - 1));
    return {
        getI: function() { return _dInicio; },
        getF: function() { return _dFim; },
        setI: function(d) { _dInicio = d; },
        setF: function(d) { _dFim = d; }
    }, callback();

 })(done);

Maybe I am using the comma operator wrong, but I think this should work. Someone could point me where i am misunderstanding something?

afjm
  • 169
  • 12

3 Answers3

3

You're calling the done function after a return, further, you're not passing the param dateFilter.

return { -> return before calling callback `function`.
    getI: function() { return _dInicio; },
    getF: function() { return _dFim; },
    setI: function(d) { _dInicio = d; },
    setF: function(d) { _dFim = d; }
}, callback( );
            ^
            |_ Calling callback without param `dateFilter`

Look at this code snippet

function done(dateFilter) {
  console.log(dateFilter.getI());
  console.log(dateFilter.getF());
}

(function(callback) {
  var _dInicio = new Date(),
    _d = new Date(),
    _dFim = new Date(_d.setMonth(new Date().getMonth() - 1));

  callback({
    getI: function() {
      return _dInicio;
    },
    getF: function() {
      return _dFim;
    },
    setI: function(d) {
      _dInicio = d;
    },
    setF: function(d) {
      _dFim = d;
    }
  });

})(done);

See? now is printing the values.

Ele
  • 33,468
  • 7
  • 37
  • 75
2

Two reasons this cannot work:

  • You are returning the result of callback with your comma operator, but done does not return anything
  • You are trying to use dateFilter during the IIFE initialisation of dateFilter - it is not yet assigned the return value

There is absolutely no reason to use a callback with an IIFE. Just write

var dateFilter = (function() {
    var _dInicio = new Date(), _d = new Date(),
        _dFim = new Date(_d.setMonth(new Date().getMonth() - 1));
    return {
        getI: function() { return _dInicio; },
        getF: function() { return _dFim; },
        setI: function(d) { _dInicio = d; },
        setF: function(d) { _dFim = d; }
    };
}());

console.log(dateFilter.getI());
console.log(dateFilter.getF());
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Actually I need to execute a function after ensure that `dateFilter` has been executed, that's why I tryied using `callback`. Should I use promies instead? – afjm Feb 06 '18 at 17:45
  • 1
    There is nothing asynchronous in here, so just placing it after the `IIFE` will ensure that it has been run. – Bergi Feb 06 '18 at 17:46
  • 1
    *If* the initialisation process was asynchronous, then yes, use promises. – Bergi Feb 06 '18 at 17:49
1

You're really close. Your hunch about the comma operator is correct. And I think there's a better way to wire up your done() callback so that the code is a bit more robust, and the order in which things happen is a bit clearer.

First, let's look at the return statement. All return statements do two things (evaluate the return value, then return it), but because of the comma, this one is doing three, in the following order:

  1. Evaluating the object literal { getI:... }
  2. evaluating callback(), which means calling callback()
  3. returning the return value of callback(), since the comma operator returns it's second operand.

So, note that callback() actually gets called before your function returns, so it happens before dateFilter has a value. (And, even if it did have a value, it would be the return value from callback(), which is undefined.)

And I think there's another aspect of your code here that I think is worth a look. Callbacks typically take parameters. The simplest way to think of it is: Instead of returning a value, you pass it to the callback.

In general, code is easier to read and debug if you pass parameters instead of side-effecting shared variables.

I just made one change to your code, and it's working:

function done(dateFilter) {
    console.log(dateFilter.getI());
    console.log(dateFilter.getF());
}

(function(callback) {
    var _dInicio = new Date(), _d = new Date(),
        _dFim = new Date(_d.setMonth(new Date().getMonth() - 1));
    callback({
        getI: function() { return _dInicio; },
        getF: function() { return _dFim; },
        setI: function(d) { _dInicio = d; },
        setF: function(d) { _dFim = d; }
    });

 })(done);

So, what did I do?

  • Instead of side-effecting the dateFilter value, I pass it directly to callback.

This means: no comma operator in the return statement, and no need to side-effect the dateFilter global value. (dateFilter is now a parameter to done isntead.)

I hope that clarifies things for you. Cheers!

DeathB4Decaf
  • 390
  • 1
  • 10
  • That clarifies a lot. I wasn't seeing how the order of the things was beeing executed before. Thanks for your answer. Now I understant better how callback and comma operator can work together. – afjm Feb 06 '18 at 18:12