Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The general case of a Future/Promise is that you can design a nicer (sometimes) API. Instead of forcing the caller to provide their own callback functions immediately when they call your function, you can return a Future instead and the client can ask for the data on that whenever. e.g.:

    function read_key(key, cb) {
      database.getItem(domain, key, cb);
    }
    ..
    read_key(key, function(err, res) { ... });
    VS.
    function read_key(key) {
      var future = Future();
      database.getItem(domain, key, future.fulfill);
      return future;
    }
    ...
    var reader = read_key(key);
    ...
    reader.when(function(err, res) { .. } );
As others have noted, you can make a generalization into Sequences that eliminates nested-callback-hell. In Node especially there are a lot of asynchronous calls (and especially especially if you are managing outside data) so they come in handy. I like this library: https://github.com/coolaj86/futures Instead of sequence-members having to return promises themselves, though, they can just call the next() function or not. As an example, you might define your program to do this:

    var obj; // shared data
    var seq = Futures.sequence();
    seq.then(function(next) { ...dosetupstuff... next(obj); })
       .then(fetchMetadataFromDB)
       .then(validateFile)
       .then(createFileInDB)
       .then(copyToOtherDB);
And you might go on. Each function relies on the previous function having completed successfully and calling next(), otherwise the sequence is broken. You can more easily create standardized error handling mechanisms as well. Of course if all your functions are synchronous this doesn't really matter, but getting at an external DB might look something like this:

    function fetchMetadataFromDB(next, obj) {
      db.fetch(query, function(result) { // success
        obj.stuff = result.whatever;
        next(obj);
      },
      function(err) { // error
        ..
      });
    }
A lot of libraries follow that pattern. Without a sequence, your code might start looking like this:

    ..setup stuff;
    function fetchMetadata(obj, cb) { .. }
    fetchMetadata(obj, function (metadata) {
        // validate file:
        validate(metadata, function(file) { // success
          // create file:
          db.create(file, function() { // success
            // copy to other db
            otherdb.copy(file, function() { // success
              ...
            }, function(err) { // copy failed
              ...
            });
          }, function(err) { // create failed
            ...
        }, function(err) { // validate failed
          ...
        });
    });
You could used named functions but that means a given named function must know the name of its successive named function, and that can make your program hard to follow and change. With the sequence approach, you have the whole sequential structure laid out at the top and can inspect functions themselves if you want to, and if a new async part enters the flow you can just insert it in the sequence. With a non-sequence named function approach, you have to jump around all the functions to get a sense of the sequence, and if a new async part enters the flow you have to change the function before to call the new function. You could define all your functions inline, which helps keep the order of execution straight and easy to follow, but adding a new async operation means adding a new level of indentation, so it's called nested-callback-hell.


Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: