5. Functions

The time has come! You've got some cool ideas bouncing around in your head, and now it's time to do some cooooooool stuff with em!


function print() {
    console.log("I printed something!!!!!!");
}

async function main() {
    print();
}

main();

What the redundant heck was that???

Let's talk about the formatting here. You're using a keyword to tell node that you're whipping up a function, which is essence, is just some reusable code. function someFunctionName() {} that's the layout. You want use the same naming styling you do with variables. Lower case for the first word, then capitalise the first letter of any other words.

When you call a function, whatever is between your function's {} is what's going to happen.

Let's talk now about that async function main() {} real quick. If you took out the line main();, your app just wouldn't do anything at all. Would just start/end with nothing happening. async is a super important thing we'll talk about a bit further down. The reason we use a main() function is to wrap up all the stuff we want to run when the app turns on. If we wrote 100 other functions, each with their own logic, we'd call them in main() so they run there. That doesn't mean you can't just call a function next to main, just good practice to wrap everything up.

function print() {
    console.log("I printed something!!!!!!");
}

async function main() {
    print();
}

main();
print(); // basically will just print again! see! reusable code!

Let's make up some more functions so we can do more cool things!

function print() {
    console.log("I printed something!!!!!!");
}

function repeat(repeats, repeatMe) {
    console.log(repeatMe.repeat(repeats));
}

async function main() {
    print();
    repeat(2, "Hello, world!");
}

main();

Alright! We're passing parameters now!

I setup a simple function to repeat a string. It requires the caller to indicate how many times to repeat a phrase, and what to repeat. We then reduntantly cheat and use the built in method for strings that repeats them.

But you start to get the idea right? We're passing in variables from wherever the function is being called from, in our function definition, we're going to give those passed in variables names to be used inside the function. This is an important distinction. You might be using variable names in main() that are not the same as what you're using in repeat() and that's ok! As long as you don't expect repeat() to know the "outside" variable names. It sort of encases itself.

function print() {
    console.log("I printed something!!!!!!");
}

function repeat(repeats, repeatMe) {
    console.log(repeatMe.repeat(repeats));
    
    // this here should break though
    console.log(stringToRepeat.repeat(numberOfTimesToRepeat));
}

async function main() {
    let numberOfTimesToRepeat = 2;
    let stringToRepeat = "Hello, world!";

    print();
    repeat(numberOfTimesToRepeat, stringToRepeat);
}

main();

You should get an error that says "'stringToRepeat' is not defined". That's because variables have scopes. In main() we declared two variable names with values. The scope where you can use those variable names is only inside of main(). In repeat() we're naming variables in between the () and settings those to the incoming values, and those variable names are scoped to work inside of repeat().

Let's try another thing or two inside of main() to help understand this scoping stuff better.

function print() {
    console.log("I printed something!!!!!!");
}

function repeat(repeats, repeatMe) {
    console.log(repeatMe.repeat(repeats));
}

async function main() {
    let numberOfTimesToRepeat = 2;
    let stringToRepeat = "Hello, world!";

    print();
    repeat(numberOfTimesToRepeat, stringToRepeat);

    function letForTheWin() {
        console.log(`Repeat "${stringToRepeat}", ${numberOfTimesToRepeat} times!`);
    }

    letForTheWin();
}

main();

So print() repeat() are happy again. They're declared outside of main, so their variables names are their own. Here, I declare a new function letForTheWin() inside of main(). Since it's inside main(), it can use the variables that have been scoped for inside of main(). If you call letForTheWin() outside of main(), do you think it'll work?


Alright, so that async business. Let's talk about some cool things that happen in JavaScript.

JS is a synchronous language. Without an insane amount of detail, that essentially means it syncs up all of the individual processes that it can, at the same time. aynsc means, do all those processes, one at a time.

This is a super important distinction.

Let's say you whipped up a couple functions. One that does a huge heavy lift of mathematical work, like multiplying pi * pi 200 times? That's going to take your computer a little longer to do than it would take your computer to print out "Hello, world!". If you have the program doing everything it can, as fast as it can, it may not get around to displaying all the different results you want to see, at least not in the order you think it will. This is where async/await comes in! If you declare that a function is async you can then create steps by using await.

Check it out

async function manyMuchPi() {
    let bigValue = 3.14;
    let pi = 3.14;

    for (let i = 0; i < 200; i++) {
        bigValue = bigValue * pi;
        bigValue = bigValue / pi;
    }

    console.log(bigValue);
}

async function hiThere() {
    console.log("Hello, world!");
}

async function main() {
    await manyMuchPi();
    await hiThere();
}

main();

We're forcing our app to wait for one function to finish before another. You're going to do this almost as a standard. If you don't async/await stuff, then when you're working with bigger chunks of data, you will see some things finish before others, and it's infuriating.


Loops. Loops, are goooooood for you.

Here's the classic for loop. It has a specific format, and it's ubiquitous across pretty much all programming languages.

async function main() {
    for(let i = 0; i < 10; i++) {
        console.log(i);
    }

    console.log("And now we're done!");
}

main();

for is baked into JS. It's a function, and expects from parameters to be handed in. The first parameter, is a brand new variable that you must declare for use in the function - let i = 0. Next up, you've got a condition to check on each loop, to see if the function should continue again - i < 10. Lastly, at the end of each loop, do something useful which is almost always count your variable upward - i++.

The output on the above will be

john.celoria@MacBook-Pro-2 hello-world % node .
0
1
2
3
4
5
6
7
8
9
And now we're done!
john.celoria@MacBook-Pro-2 hello-world %

Let's talk about the why.

let i = 0 this is declaring what's known as an iterator. Almost always it's just called i. You could name it anything, but tradition usually prevails and you'll call it i. This parameter that you pass the for loop is only done once.

i < 10 this is what happens are the start of each loop. The for function will evaluate if this statement is true before it will jump into the code block in {}. If it's false, the program is done with that loop and moves on.

i++ this adds 1 to the variable i. Simple as that! i-- would increment down. You could write this as i = i + 1 and that's legit. We're just using some short hand here.

So at the start, we make i and set it to a 0. Immediately the for loop checks to see if i is currently less than 10. If it is, we jump into the code block, which prints out the current value of i. At the end of that code block we iterate our iterator variable by 1. Back to the top! We don't change i back to 0 though. Remember, that first parameter we pass into the for() (let i = 0) only happens the once. That means we jump straight to checking if i is less than 10. Right now, since at the end of the last loop we added 1 to i, it's now the number 1! That's less than 10, so we run our code block again.

This happens over and over, and then at some point, on that last run, i is the number 9. That's less than 10, so we print out i, then iterate i to 10, and now the loop will check to see if i (which is now 10) is less than 10. It isn't, so we're done with the code block!

This for loops is so common that most languages (JS included) have made some short hand for certain situations where it's being used.

Let's say you've got an array of numbers, and you want to print out each of them. Easy. Peasy.

async function main() {
    let arr = [1,2,3,4];

    console.log("Length of the array: ", arr.length);

    for(let i = 0; i < arr.length; i++) {
        console.log(`Iterator on this run of the code block is ${i}, and the value of the array with that index is ${arr[i]}`);
    }
}

main();

Blagh my brain hurts! That's cause you forgot that array indices start at 0. So when i is 0, and we use that for the index, we get the first entry in the array. So anyways, this gives you...

john.celoria@MacBook-Pro-2 hello-world % node .
Length of the array:  4
Iterator on this run of the code block is 0, and the value of the array with that index is 1
Iterator on this run of the code block is 1, and the value of the array with that index is 2
Iterator on this run of the code block is 2, and the value of the array with that index is 3
Iterator on this run of the code block is 3, and the value of the array with that index is 4
john.celoria@MacBook-Pro-2 hello-world %

Super cool. Lots of typing though, so let's check out the short hand!!!

async function main() {
    let arr = [1,2,3,4];

    console.log("Length of the array: ", arr.length);

    for(let i in arr) {
        console.log(`Iterator on this run of the code block is ${i}, and the value of the array with that index is ${arr[i]}`);
    }
}

main();

Same exact output. Creates i and defaults to 0, then hands into your code block. Booyah.

Now what if you wanna be even a touch lazier? Check this out

async function main() {
    let arr = ["Hello there", "{ominous pause}", "General", "Kenobi"];

    console.log("Length of the array: ", arr.length);

    for (let entry of arr) {
        console.log(entry);
    }
}

main();

Here we go even faster, less typing, and I think more readable. let entry gives you the first thing in arr and passed that to your code block as a usable variable name!!! That'll repeat until there's no items in the array.

To recap:
for(let i = 0; i < someNum; i++) {} <- Super common, so common, we made it easier
for(let i in arr) {} Use against arrays to make life easier
for(let i of arr) {} Even simpler. Eveyone knows you really want the entry in the array, so there ya go.


John, you kept talking about async vs sync. Here's where you will usually find sync useful...

There's one more loop against an array that will help you out.... sometimes.

It's called forEach.

So you've got an array of things, and you gotta do something to each entry, but you don't really care the order it gets done in, you just want it to happen fast. forEach is a synchronous for loop on arrays that accomplishes this!

Let's check it out.

function piMe(number) {
    return number * 3.14;
}

async function main() {
    let arr = [];

    // let's make a nice big array the easy way. 
    for(let i = 0; i < 10; i++) {
        arr.push(i);
    }

    console.log("Length of this array, ", arr.length)

    for(i of arr) {
        console.log(piMe(i));
    }

    arr.forEach(i => {
        console.log(piMe(i));
    })
}

main();

We make an array (we're gonna make it huge in a second), then do a for loop and then a forEach loop. forEach has some syntax you haven't seen yet, it's call an "arrow function". It'll fly through each item in the array, assign that value to a variable i and is then passing that variable with the arrow => into a code block for use. It's even shorter hand for writing a function that you won't really ever reuse, but you just need in this loop!! Crazy handy right????? RIGHT?????????

On just about any modern computer in the world, you're going to get the following

john.celoria@MacBook-Pro-2 hello-world % node .
Length of this array,  10
0
3.14
6.28
9.42
12.56
15.700000000000001
18.84
21.98
25.12
28.26
0
3.14
6.28
9.42
12.56
15.700000000000001
18.84
21.98
25.12
28.26
john.celoria@MacBook-Pro-2 hello-world % 

It's exactly what you're expect. We do a for loop which waits for each loop to finish. Then we do a forEach, which passes each value sequentially into a function that prints out the value super quick. Since each operation is so simple, it sure seems like it's waiting for each previous run to complete, but it really isn't.

Plant these ideas in your head!!! They're important! We won't be into sync operations that mean anything here for a bit, but once we start working with APIs, this will be crazy important to understand!

On to Classes!!!