(Prerequisite: I/O lesson)
This is a very big topic, but briefly...
Traditional programs are written using sequences, which are performed in order like a traditional recipe, performed by a single chef:
Even though the recipe is written in a strict order, many of these steps can happen simultaneously or in parallel.
For instance, you don't have to wait for the oven to be preheated before rolling out the dough.
Or, you could have one cook rolling, cutting, and baking, and another cook removing and decorating.
NodeJS programs are written using events, which is like a bunch of cooks, each performing one part of the recipe.
The source code of the evented cookie baking program in the previous slide could just as well be written like this:
A named callback is simply a function.
Example:
console.log("what is your name?");
function sayHi(name) {
console.log('Hi, ' + name + '!');
}
process.stdin.once('data', sayHi);
This means
sayHi
that accepts a parameter named name
data
chunk, pass it in to the sayHi
function".An anonymous callback is also a function, but this time it's defined inline.
Example:
console.log("what is your name?");
process.stdin.once('data', function (name) {
console.log('Hi, ' + name + '!');
});
Note that the second argument to the once
method is the same as the entire sayHi function from the previous slide...
}
... );
You will often see the fat arrow variant syntax in anonymous callbacks.
Example:
console.log("what is your name?")
process.stdin.on('data', (name) => {
console.log('Hi, ' + name + '!');
});
Note that the second argument to the once
method is the same as the the previous slide, but with =>
after the parameter list, instead of the word function
before the parameter list.
To force events to happen in order you may need to nest your callbacks.
console.log('what is your name?')
process.stdin.once('data', (name) => {
console.log('what is your quest?')
process.stdin.once('data', (quest) => {
console.log('what is your favorite color?')
process.stdin.once('data', (color) => {
console.log('Hello ' + name + '! ' +
'Good luck with ' + quest + ', ' +
'and here is a ' + color + ' flower for you.');
process.exit();
});
});
});
Yes, nested callbacks are confusing. This is an example of callback hell.
Nested callbacks are horrible. There is a different way to do it that leads to cleaner code, but requires you to use (and understand!) Promises and Async/Await.
We will cover those topics in depth later, but for now, look at this example:
async function start() {
let name = await ask('What is your name? ');
let quest = await ask('What is your quest? ');
let color = await ask('What is your favorite color? ');
console.log('Hello ' + name + '! ' +
'Good luck with ' + quest + ', ' +
'and here is a ' + color + ' flower for you.');
}
note: for this to work, the
ask
function must return a Promise
Note that instead of passing the result in to a callback function, await
returns the result just like a normal function call so you can assign it to a variable.
Evented programs are often more flexible and high-performance than traditional sequenced programs, but they can be more confusing for humans to write and to read (and to debug!).
Also, sequences naturally end when they are finished, but evented programs will just keep doing the same things over and over again, as long as the triggers keep happening.
This means that you need to explicitly call process.exit()
in NodeJS programs.
once
is a special case of a more common method named on
. The difference is that
on
sets up an event handler that is called back any time the event occursonce
sets up the same thing, but then removes it after the first callThe following code will keep saying hello every time the user enters another line of text.
console.log("What is your name?");
process.stdin.on('data', (chunk) => {
let name = chunk.toString().trim();
console.log("Hello, " + name + "!");
});
/