2

With events, the initiator raises an event that will be received by those routines that have elected to receive that event. The receiver specifies what events it will receive from what initiators.

With callbacks, the routine after completion notifies the caller of the completion.

So I am confused where should I use events or where should I use the callbacks as I can accomplish what a callback do with events but resulting in a lot of events created in the application.

What should be a good approach to follow while coding, to use events or callbacks?

Karan Khanna
  • 1,947
  • 3
  • 21
  • 49

1 Answers1

2

Events - for things that can happen many times.

Callbacks (or promises) - for things that can happen once.

So for example, when you have a function that you call because you need a current temperature taken from some API, that function should either return a promise or take a callback that can later be called with the right value (or error).

If, on the other hand, you have a function that you call because you need to get a new temperature every time when it changes, then this function should return an event emitter (or take an event handler to attach to some internal event emitter).

Now, the question on when to use callbacks and when to use promises is a little bit more tricky because they are good for the same kinds of situations - when you want to know a result of some asynchronous operation (some data or error). Since both work for the same situations let's consider two examples of reading the contents of a file.

First, with callbacks:

let fs = require('fs');
fs.readFile('a.txt', 'utf-8', (err, data) => {
    if (err) {
        console.log('Error:', err.message);
    } else {
        console.log('Data:', data.trim());
    }
});

If there is no file it will print:

Error: ENOENT: no such file or directory, open 'a.txt'

If there is a file it will print:

Data: Contents of a.txt

Now, the same with promises:

let fs = require('mz/fs');
fs.readFile('b.txt', 'utf-8')
    .then(data => {
        console.log('Data:', data.trim());
    })
    .catch(err => {
        console.log('Error:', err.message);
    });

It works exactly the same as the previous example.

For that simple example the difference may not be very obvious but what if you wanted to have a function that abstracts some of that logic away.

For example this, with callbacks:

let fs = require('fs');
function a(cb) {
    fs.readFile('b.txt', 'utf-8', (err, data) => {
        if (err) {
            return cb('a() error: ' + err.message);
        }
        cb(null, 'a() data: ' + data.trim());
    });
}
a((err, data) => {
    if (err) {
        console.log('Error:', err);
    } else {
        console.log('Data:', data);
    }
});

It will print either this

Error: a() error: ENOENT: no such file or directory, open 'a.txt'

or something like this:

Data: a() data: Contents of a.txt

Now, what is different with promises is that you can store it in a variable, return it from a function or pass it as an argument to some other function before attaching the success/error handlers. For example:

let fs = require('mz/fs');
function a() {
    return fs.readFile('a.txt', 'utf-8')
        .then(data => 'a() data: ' + data.trim())
        .catch(err => Promise.reject('a() error: ' + err.message));
}
let promise = a();
promise.then(data => console.log('Data:', data))
       .catch(err => console.log('Error:', err));

It works the same, it is written in a different style that you may or may not find more readable, but the difference is that now you don't have to attach a callback at the time of calling the a() function. You can do it somewhere else.

If you didn't want to change the error message, it would be this with callbacks:

function a(cb) {
    fs.readFile('a.txt', 'utf-8', (err, data) => {
        if (err) {
            return cb(err);
        }
        cb(null, 'a() data: ' + data.trim());
    });

and this with promises:

function a() {
    return fs.readFile('a.txt', 'utf-8')
        .then(data => 'a() data: ' + data.trim());
}

Another difference is that if you have a function that returns a promise, you can use a new await keyword inside of a async function like this:

async function x() {
    try {
        console.log('Data:', await a());
    } catch (err) {
       console.log('Error:', err);
    }
}

You cannot use await with a function that doesn't return a promise.

It gets very convenient for example when you need to read file a.txt to get another filename that it contains, and then read that other file and print its contents while handling all errors in more complex situations.

To use async and await with Node v7.x you need to use the --harmony flag, see:

rsp
  • 107,747
  • 29
  • 201
  • 177