Javascript: understanding callbacks and event emitter
callbacks can be confusing and let’s try to understand it with an example without looking at the definition
let's take a problem as i want to ask some questions to the user using CLI and at the end of the answers I display the answers said by him..now there is any way to do it but let’s see how we can do it using callbacks
const readline = require("readline");const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});const questions = [
"What is your name? ",
"Where do you live? ",
"What are you going to do with node js? "
];const collectAnswers = (questions, done) => {
const answers = [];
const [firstQuestion] = questions;const questionAnswered = answer => {
answers.push(answer);
if (answers.length < questions.length) {
rl.question(questions[answers.length], questionAnswered);
} else {
done(answers);
}
};rl.question(firstQuestion, questionAnswered);
};collectAnswers(questions, answers => {
console.log("Thank you for your answers. ");
console.log(answers);
process.exit();
});
looks a little tricky so try to deduce the rl.question() as
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("How do you like Node? ", answer => {
console.log(`Your answer: ${answer}`);
});
the more you try to go to the depth of this code the more you will see how this is a proper mixture of callbacks with the help of arrow functions
so let’s go step by step
- collections take questions array and another arrow function which would get invoked later
- in collect answers
rl.question(firstQuestion, questionAnswered);
is being invoked and it takes the first element of questions array and takes input from cli and calledquestionAnswered
and then the question is asked until its length doesn’t match the answers array after that it goes to else condition and that’s where it exits current function and then these
console.log("Thank you for your answers. ");
console.log(answers);
process.exit();
gets performed
now coming towards event emitter. This part is pretty self-explanatory so let’s modify our code by giving output in terms of events
const readline = require("readline");
const { EventEmitter } = require("events");const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});const questions = [
"What is your name? ",
"Where do you live? ",
"What are you going to do with node js? "
];const collectAnswers = (questions, done = f => f) => {
const answers = [];
const [firstQuestion] = questions;
const emitter = new EventEmitter();const questionAnswered = answer => {
emitter.emit("answer", answer);
answers.push(answer);
if (answers.length < questions.length) {
rl.question(questions[answers.length], questionAnswered);
} else {
emitter.emit("complete", answers);
done(answers);
}
};rl.question(firstQuestion, questionAnswered);return emitter;
};const answerEvents = collectAnswers(questions);answerEvents.on("answer", answer =>
console.log(`question answered: ${answer}`)
);answerEvents.on("complete", answers => {
console.log("Thank you for your answers. ");
console.log(answers);
});answerEvents.on("complete", () => process.exit());
some more additions
const readline = require("readline");
const { EventEmitter } = require("events");const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});const collectAnswers = (questions, done = f => f) => {
const answers = [];
const [firstQuestion] = questions;
const emitter = new EventEmitter();const questionAnswered = answer => {
emitter.emit("answer", answer);
answers.push(answer);
if (answers.length < questions.length) {
emitter.emit("ask", questions[answers.length]);
rl.question(questions[answers.length], questionAnswered);
} else {
emitter.emit("complete", answers);
done(answers);
}
};process.nextTick(() => {
emitter.emit("ask", firstQuestion);
rl.question(firstQuestion, questionAnswered);
});return emitter;
};const questions = [
"What is your name? ",
"Where do you live? ",
"What are you going to do with node js? "
];const answerEvents = collectAnswers(questions);answerEvents.once("ask", () => console.log("started asking questions"));answerEvents.on("ask", question =>
console.log(` question asked: ${question}`)
);
answerEvents.on("answer", answer =>
console.log(` question answered: ${answer}`)
);
answerEvents.on("complete", answers => {
console.log("Thank you for your answers. ");
console.log(answers);
});
answerEvents.on("complete", () => process.exit());