function inside of function runs with body

To answer your question we can analyze the code example. If we read the code from top to bottom and from left to right (note that even though that is how code is parsed, that is NOT how code is executed, but we will come back to that), we first see

function add(getX,getY,cb) {
  var x, y;
  // ...
}

This is a declaration for a function named add, with 3 parameters named getX, getY and cb.

The first line inside the function body is

  var x, y;

A var keyword inside a function body means this is a function-scoped variable declaration. This just means that x and y are declared from the start to the end of the add function body (the var keyword can also be used for globally-scoped variable declarations, there is also the let keyword for block-scoped variable declaration).

The next code part is

  getX( function(xVal){
    x = xVal;
    if (y != undefined) {
       cb(x + y );
    }
  });

This starts with a call to getX(...); (remember getX is the first parameter of the add function that we are in). So getX has to be something that we can call, i.e. a function, otherwise we will get a runtime error.

The first (and only) input parameter that is passed to the getX call is

  function(xVal){
    x = xVal;
    if (y != undefined) {
       cb(x + y );
    }
  }

which is a function expression.

The function keyword can be used to define a function inside an expression.

Using a function keyword in this way (or using an arrow function expression) without specifying a name, defines an anonymous function. You might hear others refer to them (depending on context) as callbacks/lambdas/closures.

So this tells us that the getX function should accept a function as the first parameter and potentially call it as part of its internal logic.

We also notice that this anonymous function function(xVal){ ... } accepts a single parameter xVal. This tells us that the getX function should pass some value as the first parameter when (if) it calls this function.

So if the getX function ever calls back this anonymous function that we are passing as its first parameter, then the body of this anonymous function will be executed. If the getX function never calls it, then the body of this anonymous function will never be executed.

The body of the anonymous function is

    x = xVal;
    if (y != undefined) {
       cb(x + y );
    }

which just assigns xVal to x and conditionally calls cb(x + y), i.e. the third parameter of the add function, which is still the outer function scope here.

We could do a similar breakdown for the getY call. The only difference would be that it calls the getY function parameter and the body of the anonymous function assigns yVal to y (but it still conditionally calls cb(x + y)).

The last few lines of the code example are

// fetchX() and fetch() are sync or async functions
add(fetchX, fetchY, function(sum) {
  console.log(sum);
});

So this is a call of the previously declared named function function add(getX, getY, cb) { ... }.

When this code line is executed it will call add with

  • 1st parameter: fetchX
  • 2nd parameter: fetchY
  • 3rd parameter: an anonymous function function(sum) { console.log(sum); }. This anonymous function accepts a single parameter sum and only when (if) called it logs it to the console.

  1. what is the point of passing in fetchX and fetchY?

As we see from the breakdown above, fetchX and fetchY are passed into the add function call in order for them to serve as "replacements" for calls to getX and getY inside the function body. You can think of it that the call to getX(...) calls fetchX(...) and the call to getY(...) calls fetchY(...).

They will be "called back" from inside the function, that's why in such cases we call them callbacks, they are functions passed as parameters into another function, and said other function might call them based on its internal logic. In your example they are called once each (assuming no errors happen during runtime).


  1. How does getX and getY run? I can imagine running w/ getX() and getY() but code has getX(){} and getY(){} and that looks more like function declaration?

As mentioned earlier the code is not always executed from top to bottom and from left to right. So after parsing the code from the example, the code interpreter will execute the add call first (because the rest is just a declaration for the add function) and it will pass fetchX, fetchY and function(sum){ console.log(sum); } as the parameters. Right after the call happens the execution continues with the add function body.

Right from the getX call inside the add function body there are different possible flows, depending on what the logic inside the getX and getY is (specifically fetchX and fetchY in your case). Since we don't have a documentation for fetchX and fetchY nor their source code, we cannot know how the code will execute from here on. But we can explore some of the possible flows.

I added a few console.log statements to your original code example. In that way we can see the order of the execution. Another way to do this, without the need to add extra log statements, is to use the debugger, place a breakpoint somewhere inside the function and then use the step commands to step through the code as it executes.

Option 1: If both fetchX and fetchY call their first parameter (the callback they receive) always exactly once synchronously then the flow is (run the code snippet)

// These two callbacks could be defined elsewhere, maybe even in code not under our control, such as in a library...
fetchX = function(callback) {
  callback(2); // unconditional single synchronous call
};
fetchY = function(callback) {
  callback(3); // unconditional single synchronous call
};

console.log("Entering the code example scope");

function add(getX,getY,cb) {
  console.log("Called: function add(getX,getY,cb)");
  var x, y;
  getX( function(xVal){
console.log("Called: function(xVal) xVal =", xVal);
x = xVal;
if (y != undefined) {
   cb(x + y);
}
  });
  getY( function(yVal){
console.log("Called: function(yVal) yVal =", yVal);
y = yVal;
if (x != undefined) {
   cb(x + y);
}
  });
  console.log("Returning from: function add(getX,getY,cb)");
}

add(fetchX, fetchY, function(sum) {
  console.log("Called: function(sum)");
  console.log(sum);
  console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");

Option 2: If fetchX calls its callback always exactly once synchronously and fetchY calls its callback parameter always exactly once asynchronously, then the flow is (run the code snippet)

fetchX = function(callback) {
  callback(2); // unconditional single synchronous call
};
fetchY = function(callback) {
  setTimeout(function() { callback(3); }, 0); // unconditional single asynchronous call
};

console.log("Entering the code example scope");

// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
  console.log("Called: function add(getX,getY,cb)");
  var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x + y); } }); getY( function(yVal){  console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x + y); } });
  console.log("Returning from: function add(getX,getY,cb)");
}

add(fetchX, fetchY, function(sum) {
  console.log("Called: function(sum)");
  console.log(sum);
  console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");

Option 3: If fetchX calls its callback always exactly once asynchronously and fetchY calls its callback parameter always exactly once synchronously, then the flow is (run the code snippet)

fetchX = function(callback) {
  setTimeout(function() { callback(2); }, 0); // unconditional single asynchronous call
};
fetchY = function(callback) {
  callback(3); // unconditional single synchronous call
};

console.log("Entering the code example scope");

// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
  console.log("Called: function add(getX,getY,cb)");
  var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x + y); } }); getY( function(yVal){  console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x + y); } });
  console.log("Returning from: function add(getX,getY,cb)");
}

add(fetchX, fetchY, function(sum) {
  console.log("Called: function(sum)");
  console.log(sum);
  console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");

Option 4: If both fetchX and fetchY call their callback always exactly once asynchronously and fetchX calls it sooner, then the flow is (run the code snippet)

fetchX = function(callback) {
  setTimeout(function() { callback(2); }, 0); // unconditional single asynchronous call
};
fetchY = function(callback) {
  setTimeout(function() { callback(3); }, 50); // unconditional single asynchronous call (further delayed)
};

console.log("Entering the code example scope");

// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
  console.log("Called: function add(getX,getY,cb)");
  var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x + y); } }); getY( function(yVal){  console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x + y); } });
  console.log("Returning from: function add(getX,getY,cb)");
}

add(fetchX, fetchY, function(sum) {
  console.log("Called: function(sum)");
  console.log(sum);
  console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");

Option 5: If both fetchX and fetchY call their callback always exactly once asynchronously and fetchY calls it sooner, then the flow is (run the code snippet)

fetchX = function(callback) {
  setTimeout(function() { callback(2); }, 50); // unconditional single asynchronous call (further delayed)
};
fetchY = function(callback) {
  setTimeout(function() { callback(3); }, 0); // unconditional single asynchronous call
};

console.log("Entering the code example scope");

// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
  console.log("Called: function add(getX,getY,cb)");
  var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x + y); } }); getY( function(yVal){  console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x + y); } });
  console.log("Returning from: function add(getX,getY,cb)");
}

add(fetchX, fetchY, function(sum) {
  console.log("Called: function(sum)");
  console.log(sum);
  console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");

Option 6: If fetchX calls its callback exactly three times asynchronously on an interval of 30ms with values 10, 20, 30 and fetchY calls its callback exactly three times asynchronously on an interval of 50ms with values 1, 2, 3, then the flow is (run the code snippet)

fetchX = function(callback) {
  setTimeout(function() { callback(10); }, 30);
  setTimeout(function() { callback(20); }, 60);
  setTimeout(function() { callback(30); }, 90);
};
fetchY = function(callback) {
  setTimeout(function() { callback(1); },  50);
  setTimeout(function() { callback(2); }, 100);
  setTimeout(function() { callback(3); }, 150);
};

console.log("Entering the code example scope");

// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
  console.log("Called: function add(getX,getY,cb)");
  var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x + y); } }); getY( function(yVal){  console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x + y); } });
  console.log("Returning from: function add(getX,getY,cb)");
}

add(fetchX, fetchY, function(sum) {
  console.log("Called: function(sum)");
  console.log(sum);
  console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");

Here are the outputs of the above options

Option 1:

Entering the code example scope
Called: function add(getX,getY,cb)
Called: function(xVal) xVal = 2
Called: function(yVal) yVal = 3
Called: function(sum)
5
Returning from: function(sum)
Returning from: function add(getX,getY,cb)
Leaving the code example scope

Option 2:

Entering the code example scope
Called: function add(getX,getY,cb)
Called: function(xVal) xVal = 2
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(yVal) yVal = 3
Called: function(sum)
5
Returning from: function(sum)

Option 3:

Entering the code example scope
Called: function add(getX,getY,cb)
Called: function(yVal) yVal = 3
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(xVal) xVal = 2
Called: function(sum)
5
Returning from: function(sum)

Option 4:

Entering the code example scope
Called: function add(getX,getY,cb)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(xVal) xVal = 2
Called: function(yVal) yVal = 3
Called: function(sum)
5
Returning from: function(sum)

Option 5:

Entering the code example scope
Called: function add(getX,getY,cb)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(yVal) yVal = 3
Called: function(xVal) xVal = 2
Called: function(sum)
5
Returning from: function(sum)

Option 6:

Entering the code example scope
Called: function add(getX,getY,cb)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(xVal) xVal = 10
Called: function(yVal) yVal = 1
Called: function(sum)
11
Returning from: function(sum)
Called: function(xVal) xVal = 20
Called: function(sum)
21
Returning from: function(sum)
Called: function(xVal) xVal = 30
Called: function(sum)
31
Returning from: function(sum)
Called: function(yVal) yVal = 2
Called: function(sum)
32
Returning from: function(sum)
Called: function(yVal) yVal = 3
Called: function(sum)
33
Returning from: function(sum)

There are of course more possible flows. Either of the functions could synchronously or asynchronously call its callback multiple times (not just a single time), or it could call it on an interval forever or it could call it conditionally - so sometimes not even once.

As you can see from all the possibilities above, without the documentation or source code for functions fetchX and fetchY, it is impossible to determine what the flow is. Also the flow will dynamically change based on what functions are passed in.

You can just copy any of the code snippets above and change the logic inside fetchX and fetchY and explore yourself other flows that you are interested in.