A filesystem example of callbacks

The following code has no guarantee of working because writeFile returns immediately and rename may happen first.

const fs = require('fs');

fs.writeFile('data.txt', JSON.stringify({foo: "bar"}), function (err) {
	if (err) throw err;
	console.log("wrote the file!");
});

fs.rename('data.txt', 'data.json', (err) => {
	if (err) throw err;
	console.log("renamed .txt to .json");
})

The following code will end in an error because we aren’t properly controlling the flow of execution with our callbacks. In this case, we have forced the write to happen after the rename.

const fs = require('fs');

setTimeout(() => {
	fs.writeFile('data.txt', JSON.stringify({foo: "bar"}), function (err) {
		if (err) throw err;
		console.log("wrote the file!");
	});
}, 500);

fs.rename('data.txt', 'data.json', (err) => {
	if (err) throw err;
	console.log("renamed .txt to .json");
})

The following has correct behavior because the call for rename is nested in the callback for writeFile.

const fs = require('fs');

setTimeout(() => {
	fs.writeFile('data.txt', JSON.stringify({foo: "bar"}), function (err) {
		if (err) throw err;
		console.log("wrote the file!");


		fs.rename('data.txt', 'data.json', (err) => {
			if (err) throw err;
			console.log("renamed .txt to .json");
		})

	});
}, 500);

A new library-based version of this code

lib/fsexample.js
const fs = require('fs');

function MyFSLib(file) {
    this.file = file;
}

MyFSLib.prototype.write = function(data, cb) {
    let jsonData = JSON.stringify(data);
    fs.writeFile(this.file, jsonData, function (err) {
        if (err) cb(err);
        console.log('wrote data file');
        cb();
    });
}

MyFSLib.prototype.rename = function(newName, cb) {
    fs.rename(this.file, newName, (err) => {
        if (err) cb(err);
        this.file = newName;
        console.log('file is renamed!');
        cb();
    });
}

module.exports = MyFSLib;
index.js
const MyFSLib = require('./lib/fsexample.js');

let f = new MyFSLib('data.txt');

f.write({foo: 'bar'}, (err) => {
    if (err) throw err;
    console.log('write is finished');
    f.rename('data.json', (err) => {
        if (err) throw err;
        console.log('rename is finished');
    });

    console.log('line after rename');
});

console.log('line after write');

A callback/evented example

This example is largely from the book.

var events = require('events');

function CarShow() {
	events.EventEmitter.call(this);
	this.seeCar = function(make) {
		this.emit('sawCar', make);
	};
}
CarShow.prototype.__proto__ = events.EventEmitter.prototype;

var show = new CarShow();

function logCar(make, callback) {
	console.log("I saw a " + make);
	callback(make);
}

show.on("sawCar", function(make) {
	logCar(make, function(make, color) {
		var colors = ['red','green','blue'];
		var color = colors[Math.floor(Math.random()*3)];
		console.log("I saw a " + make + " colored " + color);
	});
});

show.seeCar("Honda");
show.seeCar("Toyota");
show.seeCar("Centurion");