Javascript: understanding callbacks and event emitter

Prakash Jha
3 min readApr 28, 2021

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

  1. collections take questions array and another arrow function which would get invoked later
  2. 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 called questionAnswered 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());

--

--