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?

2

6 Answers 6

6

You could use async module to avoid this.

Sign up to request clarification or add additional context in comments.

Comments

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))
    }
})()

4 Comments

ah, that was why it wasn't syntax highlighting properly. Fixed.
@nornagon, that doesn't change my answer.
@nornagon, there is a very simple solution for that, I have updated my example.
This code is invalid. Ironic. You cannot immediately invoke an unnamed function declaration.
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.

Comments

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.

Comments

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();

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.