Callback Hell Converted To Promises Chaining With Modules & Passing Data

Callback Hell Converted To Promises Chaining With Modules & Passing Data

I wanted to log my take on callback hell as I couldn’t really find much information on how to pass data through your callback chain after being changed to promises, it turns out it is pretty easy when you know how and the results are great crazy looking code looks very nice.

I do a lot of data analysis with ffmpeg, ffprobe and I want to give a useful example of how to do this but using promises.

I also want to have my functions in a helper file so they are separate and not all bundled in one file.

We are going to select a media file and grab analyis its data using ffprobe then we will simply read the file using Node fs filesystem.

New Promise Code

Here is the final code.

index.js

var fs = require('fs');

const pro = require('./helpers');

var data = {
    file: '/Users/picca/Desktop/out.mp4'
};

pro.probe(data)
	.then((data) => pro.read(data))
	.then(function (res) {
		console.log("res", res);
	})
	.catch(function (err) {
		console.log(err);
	});

And here is the helper.js

var fs = require('fs');

const probe = (data) => {

    return new Promise((resolve, reject) => {

        require('child_process').execFile('/usr/local/bin/ffprobe', [
            '-show_format',
            '-show_streams',
            '-select_streams',
            'a',
            '-of',
            'json',
            '-loglevel',
            'error',
            data.file
        ], (err, stdout, stderr) => {

            if (err) return reject(err);
            
            data.probe_data = JSON.parse(stdout);
            
            resolve(data);


        });

    });

};

const read = (data) => {

    return new Promise((resolve, reject) => {
    
        fs.readFile(data.file, {}, (err, res) => {

            if (err) return reject(err)
            
            data.read_data = res;
            
            resolve(data)
        
        });
    
    });

}

exports.probe = probe;
exports.read = read;

We are passing the main file through the chain and adding to the data array as it goes then we will get a full result within our then function.

Best Method Async Await

By far the best method and easiest to read is using async and await.

var fs = require("fs");

const pro = require("./helpers");

var data = {
	file: "/Users/samueleast/Desktop/intro.mp4"
};

async function init() {
	const probe_data = await pro.probe(data);
	console.log(probe_data);
	const read_data = await pro.read(probe_data);
	console.log(read_data);
}

init().catch(function (res) {
	console.log("Error", res);
});
Previous Code

This was the previous code.

var fs = require('fs');

function probe(data, callback) {

    require('child_process').execFile('/usr/local/bin/ffprobe', [
        '-show_format',
        '-show_streams',
        '-select_streams',
        'a',
        '-of',
        'json',
        '-loglevel',
        'error',
        data.file
    ], (err, stdout, stderr) => {

        if (err) {

            callback({
                error: true,
                message: err
            });

        } else {

            data.probe_data = JSON.parse(stdout);

            callback({
                error: false,
                message: err
            });

        }

    });

}

function read(data, callback) {

    fs.readFile(data.file, {}, (err, res) => {

        if (err) {

            callback({
                error: true,
                message: err
            });

        } else {

            data.read_data = res;

            callback({
                error: false,
                message: err
            });

        }

    });

}


var data = {
    file: '/Users/picca/Desktop/out.mp4'
};

probe(data, function(res) {

    read(res, function(res) {

        console.log(res);

    });

});

At first glance it might not seem that different but we are only chaining two functions together here with multiple callbacks of five or more you really start to see the improvement.

Leave a comment

Your email address will not be published. Required fields are marked *