9

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?

nornagon
  • 15,393
  • 18
  • 71
  • 85

6 Answers6

6

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))
    }
})()
mikerobi
  • 20,527
  • 5
  • 46
  • 42
6

You could use async module to avoid this.

Alfred
  • 60,935
  • 33
  • 147
  • 186
5

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.

nornagon
  • 15,393
  • 18
  • 71
  • 85
1

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();
Pappa
  • 1,593
  • 14
  • 20
1

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.

George
  • 11
  • 1
0

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

phani
  • 1,134
  • 1
  • 11
  • 24