Callback/Helper Functions - JavaScript Syntax II

I posted this in Discord as well, forgive the duplication:

I’m going through the iterators section of the JavaScript Syntax lesson in the full-stack path, and callback functions have been introduced as part of the lesson. Is this another name for helper functions?

I feel like I grasp the iterator methods, but I don’t quite “get” callback methods to my satisfaction.

I understand the utility of passing in the return value of a function as an argument of another function, but I don’t understand the utility of the arrow body declarations being used in the iterator method exercises.

e.g. const smallNumbers = bigNumbers.map(divideBy => {return divideBy / 100});

That doesn’t so much seem like a callback as it does just another way to declare a function.

I guess what I’m saying is I don’t understand the purpose of using a callback in these scenarios if both the higher order function and the callback aren’t fully-fledged and predefined functions?

Can someone clarify why this is the preferred syntax for utilizing iterator methods? I can reproduce it through rote memorization, but I would much prefer being able to fully comprehend the why of it so I can put it to better use. I haven’t encountered an explanation in the Duckett text so far either.

In one sense, yes, though it applies more specifically to a function that is recalled several times in the context of the iterator.

const timesTwo = x => x * 2;

const array = [4, 5, 6, 7, 8]
const doubleArray = array.map(timesTwo)

console.log(doubleArray)    //  [ 8, 10, 12, 14, 16 ]

timesTwo is a helper function that returns twice the input value.

doubleArray is derived (mapped) from array with the helper function acting as a callback the map iterator invokes with each value in the context array.

Iterators have the iteration built in, so we just have to keep that in mind. This is the same as a for loop that builds one array by iterating over another one.

const array = [4, 5, 6, 7, 8]
const doubleArray = []
for (let x of array) {
  doubleArray.push(timesTwo(x))
}

In this instance, timesTwo really is a helper function because it is not a callback. push() does not expect a function; it expects a value.

There is nothing to say we cannot or should not define the callback function right in the argument for .map().

doubleArray = array.map(x => x * 2)

Only thing here is it is a one-off thing. By having the function stand on its own, all parts of the program can use it, rather than replicating it similar to above.

1 Like

Thanks for the clarification. Excellently explained. Especially this:

In one sense, yes, though it applies more specifically to a function that is recalled several times in the context of the iterator.

So to make sure I understand - the defining feature of a callback is that it’s used as an argument of an iterator method and gets called several times as the iterator loops over the array. It does not have to be used as an argument of another function in order to be a callback function (although it can be)?

I think the issue with the intro to callbacks in the reading is that it focuses pretty narrowly on the fact that callbacks are functions used as arguments in other functions, when in fact that’s only tangential to their purpose:

We call the functions that get passed in as parameters and invoked callback functions because they get called during the execution of the higher-order function.

Based on the above, I was perplexed as to the purpose of callback functions when later in the exercises they were being used without being passed in as an argument of another function. Is this still strictly accurate because even when expressed as an anonymous function, the iterator method is still technically a function; just a function that’s a property of the array object? So the anonymous callback function is an argument of the iterator method?

What is the distinction between helper functions and callbacks in terms of being invoked? The reading suggests that invoking a function evaluates to its return value while not invoking it only passes the body of the function. What’s the difference?

Thanks again!

1 Like

Precisely, along with the understanding that the iterator will invoke the callback. When we pass the function, it is not yet invoked. We are not passing in a return value from the function, but a reference to that function which the iterator can invoke in its own time, as many times as necessary (which part you appear to understand).

Consider another use case of higher order functions…

print (timesTwo(timesTwo(timesTwo(2))))

What will that print? Note that this is not a callback, just a function taking the return from another (invoked) function. Did you get 16?

Earlier we mentioned the helper function. Because we wrote it as a standalone we were able to invoke it in the wild. Written as an anonymous function in the argument of the iterator would have taken it out of global scope. It would be truly anonymous since only the iterator knows it’s there.

So,

context.iterator(x => x * 2)    //  anonymous callback
context.iterator(timesTwo)      //  callback reference

timesTwo(42)    //  basic invocation of helper function

It does not actually pass the body of the function, but a reference to it. Much like the following…

const print = console.log

print is not a copy of the function. It points to the same location as console.log, so is able to invoke it under a different name and expect the exact same behavior. This is because console.log is a reference, not a value. We can invoke print() anywhere we could invoke console.log().

The difference is simply whether we invoke the function as we pass it in, yielding a return value, or we pass only a reference which can be later invoked by the function we give it to (the iterator).

Another place we use callbacks is in managing DOM events (among others). When we register listeners we give them a handler function in the form of a callback. When the event is detected, it triggers the callback on the event object. You’ll see more of this in the Events unit. Same basic idea, though. Pass a function by reference.

const runFunc(obj, func) {
    for (let x of obj) {
        func(x)
    }
}
runFunc(array, print) 
4
5
6
7
8

You’ll see the above form as you continue with this unit.

1 Like