nornagon
nornagon

Reputation: 15821

How do I avoid deeply nested code in node.js?

In node.js, it being event-driven, all I/O is done via callbacks. So I end up writing code that looks like this:

app.get('/test', function (req, res) {
  http.get('some/place', function (req1, res1) {
    if (res1.statusCode == 200) {
      res1.on('data', function (data) {
        http.get('other/place?q=' + data, function (req2, res2) {
          if (res2.statusCode == 200) {
            res2.on('data', function (data) {
              db.query(data).on('data', function (rows) {
                res.writeHead(200)
                res.end(JSON.stringify(rows))
              })
            })
          }
        })
      })
    }
  })
})

And that doesn't even include error handling.

What can I do to unwind this mess?

Upvotes: 9

Views: 3814

Answers (6)

phani
phani

Reputation: 1134

You can use promises. Check this out https://github.com/kriskowal/q

Upvotes: 0

Pappa
Pappa

Reputation: 1645

Another (and perhaps better) way to clean up this kind of thing is to use EventEmitters rather than callbacks. Here's an example showing several EventEmitters in use:

    var events = require('events'),
    util = require('util');

    var Search = function () {
    "use strict";

    var search,
        requestPageData,
        makeReturnObject,
        sendResults,

        emptyObj = {};

    events.EventEmitter.call(this);

    search = function (query) {
        this.emit("requestPageData", query);
    };

    requestPageData = function (query) {
        var resultsArray = [];

        // some logic

        if (.....some condition....) {
            this.emit("makeReturnObject", resultsArray);
        } else {
            this.emit("sendResults", emptyObj);
        }
    };

    makeReturnObject = function (resultsArray) {
        var resultsObj = {};

        if (magnetArray) {

            // some logic

            this.emit("sendResults", resultsObj);
        } else {
            this.emit("sendResults", emptyObj);
        }
    };

    sendResults = function (obj) {
        // finally, do whatever it is you want to do with obj
    };

    this.on("requestPageData", requestPageData);

    this.on("makeReturnObject", makeReturnObject);

    this.on("sendResults", sendResults);

    this.search = search;

};

util.inherits(Search, events.EventEmitter);
module.exports = new Search();

Upvotes: 1

mikerobi
mikerobi

Reputation: 20878

Don't use anonymous functions.

EDIT

Your code isn't even valid. You aren't closing most of your function calls.

If you switched to named functions it would look something like this: Updated to reflected comment about global namespace

(function () {
    app.get('/test', f0)

    function f0(req, res) {
      http.get('some/place', f1)
    }

    function f1(req1, res1) {
        if (res1.statusCode == 200) {
          res1.on('data', f2)
        }
     }
    function f2(data) {
        http.get('other/place?q=' + data, f3)
    }
    function f3(req2, res2) {
      if (res2.statusCode == 200) {
        res2.on('data', f4)
      }
    }

    function f4(data) {
          db.query(data).on('data', f5)
        }

    function f5(rows) {
        res.writeHead(200)
        res.end(JSON.stringify(rows))
    }
})()

Upvotes: 6

George
George

Reputation: 11

Please have a look at Streamline; it is a JavaScript preprocessor that allows you to write simple 'streamlined' code and transforms it to callback-heavy code.

Upvotes: 1

nornagon
nornagon

Reputation: 15821

I wrote a library based on node-seq, which looks like this:

app.get('/test', function (req, res) {
  Seq()
    .seq(function () {
      http.get('some/place', this.next)
    })
    .seq(function (req1, res1) {
      if (res1.statusCode == 200) {
        res1.on('data', this.next)
      }
    })
    .seq(function (data) {
      http.get('other/place?q=' + data, this.next)
    })
    .seq(function (req2, res2) {
      if (res2.statusCode == 200) {
        res2.on('data', this.next)
      }
    })
    .seq(function (data) {
      db.query(data).on('data', this.next)
    })
    .seq(function (rows) {
      res.writeHead(200)
      res.end(JSON.stringify(rows))
    })
})

The code is here.

Also, there's a lengthy discussion on the nodejs mailing list about this issue.

Step is another library for doing this stuff.

Upvotes: 5

Alfred
Alfred

Reputation: 61793

You could use async module to avoid this.

Upvotes: 6

Related Questions