Как избежать длительного вложения асинхронных функций в Node.Яш

Я хочу сделать страницу, которая отображает некоторые данные из БД, поэтому я создал некоторые функции, которые получают эти данные из моей БД. Я всего лишь новичок в узле.js, насколько я понимаю, если я хочу использовать их все на одной странице (HTTP-ответ), мне придется вложить их все:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

если таких функций много, то вложение становится проблемой.

есть ли способ избежать этого? Я думаю, это связано с тем, как вы объединяете несколько асинхронные функции, которые кажутся чем-то фундаментальным.

23 ответов


интересное наблюдение. Обратите внимание, что в JavaScript вы обычно можете заменить встроенные анонимные функции обратного вызова именованными переменными функций.

следующее:

http.createServer(function (req, res) {
   // inline callback function ...

   getSomeData(client, function (someData) {
      // another inline callback function ...

      getMoreData(client, function(moreData) {
         // one more inline callback function ...
      });
   });

   // etc ...
});

может быть переписан, чтобы выглядеть примерно так:

var moreDataParser = function (moreData) {
   // date parsing logic
};

var someDataParser = function (someData) {
   // some data parsing logic

   getMoreData(client, moreDataParser);
};

var createServerCallback = function (req, res) {
   // create server logic

   getSomeData(client, someDataParser);

   // etc ...
};

http.createServer(createServerCallback);

однако, если вы не планируете повторно использовать логику обратного вызова в других местах, часто гораздо проще читать встроенные анонимные функции, как в вашем примере. Это также избавит вас от необходимости найти имя для всех обратный вызов.

кроме того, обратите внимание, что as @pst отмечено в комментарии ниже, если вы получаете доступ к переменным закрытия во внутренних функциях, вышеуказанное не будет простым переводом. В таких случаях использование встроенных анонимных функций еще более предпочтительно.


Kay, просто используйте один из них модули.

получится это:

dbGet('userIdOf:bobvance', function(userId) {
    dbSet('user:' + userId + ':email', 'bobvance@potato.egg', function() {
        dbSet('user:' + userId + ':firstName', 'Bob', function() {
            dbSet('user:' + userId + ':lastName', 'Vance', function() {
                okWeAreDone();
            });
        });
    });
});

в:

flow.exec(
    function() {
        dbGet('userIdOf:bobvance', this);

    },function(userId) {
        dbSet('user:' + userId + ':email', 'bobvance@potato.egg', this.MULTI());
        dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
        dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());

    },function() {
        okWeAreDone()
    }
);

по большей части, я бы согласился с Даниэлем Вассалло. Если вы можете разбить сложную и глубоко вложенную функцию на отдельные именованные функции, то это обычно хорошая идея. Для случаев, когда имеет смысл делать это внутри одной функции, вы можете использовать один из многих узлов.доступны асинхронные библиотеки js. Люди придумали много разных способов справиться с этим, поэтому взгляните на узел.страница с JS модули и посмотреть, что вы думаете.

Я написал модуль для этого себя, называемого асинхронность.js. Используя это, приведенный выше пример можно обновить до:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  async.series({
    someData: async.apply(getSomeDate, client),
    someOtherData: async.apply(getSomeOtherDate, client),
    moreData: async.apply(getMoreData, client)
  },
  function (err, results) {
    var html = "<h1>Demo page</h1>";
    html += "<p>" + results.someData + "</p>";
    html += "<p>" + results.someOtherData + "</p>";
    html += "<p>" + results.moreData + "</p>";
    res.write(html);
    res.end();
  });
});

одна хорошая вещь в этом подходе заключается в том, что вы можете быстро изменить свой код, чтобы получить данные параллельно, изменив функцию "series" на "parallel". Более того, асинхронный.js будет также работают внутри браузера, так что вы можете использовать те же методы, что и в узле.js, если вы столкнетесь с каким-либо сложным асинхронным кодом.

надеюсь, что это полезно!


Вы можете использовать этот трюк с массивом, а не вложенные функции или модуля.

гораздо легче на глазах.

var fs = require("fs");
var chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step3");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step4");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step5");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("done");
    },
];
chain.shift()();

вы можете расширить идиому для параллельных процессов или даже параллельных цепочек процессов:

var fs = require("fs");
var fork1 = 2, fork2 = 2, chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        var next = chain.shift();
        fs.stat("f2a.js",next);
        fs.stat("f2b.js",next);
    },
    function(err, stats) {
        if ( --fork1 )
            return;
        console.log("step3");
        var next = chain.shift();

        var chain1 = [
            function() { 
                console.log("step4aa");
                fs.stat("f1.js",chain1.shift());
            },
            function(err, stats) { 
                console.log("step4ab");
                fs.stat("f1ab.js",next);
            },
        ];
        chain1.shift()();

        var chain2 = [
            function() { 
                console.log("step4ba");
                fs.stat("f1.js",chain2.shift());
            },
            function(err, stats) { 
                console.log("step4bb");
                fs.stat("f1ab.js",next);
            },
        ];
        chain2.shift()();
    },
    function(err, stats) {
        if ( --fork2 )
            return;
        console.log("done");
    },
];
chain.shift()();

мне нравится асинхронность.js много для этой цели.

проблема решена командой водопада:

водопад(задачи, [вызов])

запускает массив функций последовательно, каждый из которых передает свои результаты следующему в массиве. Однако, если какая-либо из функций передает ошибку обратному вызову, следующая функция не выполняется, и основной обратный вызов немедленно вызывается с ошибкой.

Аргументы

задачи-массив функций для запуска, каждой функции передается обратный вызов(err, result1, result2,... она должна быть завершена. Первый аргумент-это ошибка (которая может быть null), и любые дальнейшие аргументы будут переданы в качестве аргументов для следующей задачи. callback (err, [results]) - необязательный обратный вызов для запуска после завершения всех функций. Это будет передано результаты обратного вызова последней задачи.

пример

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

как для переменные req,res, они будут совместно использоваться в той же области, что и функция (req,res) {}, которая заключила в себя всю асинхронность.водопад зовет.

не только так, async очень чистый. Я имею в виду, что я меняю много таких случаев:

function(o,cb){
    function2(o,function(err, resp){
        cb(err,resp);
    })
}

первый:

function(o,cb){
    function2(o,cb);
}

потом так:

function2(o,cb);

потом так:

async.waterfall([function2,function3,function4],optionalcb)

Это также позволяет многие готовые функции, подготовленные для асинхронного вызова из util.js очень быстро. Просто цепь, что вы хотите сделать, убедитесь, что o, cb универсально обрабатывается. Это значительно ускоряет весь процесс кодирования.


что вам нужно, это немного синтаксического сахара. Проверьте это:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = ["<h1>Demo page</h1>"];
  var pushHTML = html.push.bind(html);

  Queue.push( getSomeData.partial(client, pushHTML) );
  Queue.push( getSomeOtherData.partial(client, pushHTML) );
  Queue.push( getMoreData.partial(client, pushHTML) );
  Queue.push( function() {
    res.write(html.join(''));
    res.end();
  });
  Queue.execute();
}); 

довольно опрятный, не так ли? Вы можете заметить, что html стал массивом. Это отчасти потому, что строки неизменяемы, поэтому вам лучше буферизировать вывод в массиве, чем отбрасывать все большие и большие строки. Другая причина - из-за другого хорошего синтаксиса с bind.

Queue в примере действительно просто пример и вместе с partial может быть реализовано следующим образом

// Functional programming for the rescue
Function.prototype.partial = function() {
  var fun = this,
      preArgs = Array.prototype.slice.call(arguments);
  return function() {
    fun.apply(null, preArgs.concat.apply(preArgs, arguments));
  };
};

Queue = [];
Queue.execute = function () {
  if (Queue.length) {
    Queue.shift()(Queue.execute);
  }
};

Я люблю асинхронность.js С тех пор, как я нашел его. У него есть async.series функция, которую вы можете использовать, чтобы избежать длительного вложения.

документация:-



самый простой синтаксический сахар, который я видел, - это node-promise.

узел установки npm-promise / / git clone https://github.com/kriszyp/node-promise

используя это, вы можете связать асинхронные методы как:

firstMethod().then(secondMethod).then(thirdMethod);

возвращаемое значение каждого из них доступно в качестве аргумента в следующем.


то, что вы сделали, - это взять шаблон asynch и применить его к 3 функциям, вызываемым последовательно, каждая из которых ждет завершения предыдущей перед запуском, т. е. вы их сделали синхронно. Суть программирования asynch в том, что вы можете иметь несколько функций, запущенных одновременно, и не ждать завершения каждой из них.

Если getSomeDate() ничего не предоставляет getSomeOtherDate (), который ничего не предоставляет getMoreData() тогда почему вы не называете их асинхронно, как позволяет js, или если они взаимозависимы (а не асинхронны), напишите их как одну функцию?

вам не нужно использовать вложенность для управления потоком - например, получить каждую функцию для завершения, вызвав общую функцию, которая определяет, когда все 3 завершены, а затем отправляет ответ.


полагаю, вы могли бы сделать это:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var html = "<h1>Demo page</h1>";
    chain([
        function (next) {
            getSomeDate(client, next);
        },
        function (next, someData) {
            html += "<p>"+ someData +"</p>";
            getSomeOtherDate(client, next);
        },
        function (next, someOtherData) {
            html += "<p>"+ someOtherData +"</p>";
            getMoreData(client, next);
        },
        function (next, moreData) {
            html += "<p>"+ moreData +"</p>";
            res.write(html);
            res.end();
        }
    ]);
});

вам нужно только реализовать chain () так, чтобы он частично применял каждую функцию к следующей и немедленно вызывал только первую функцию:

function chain(fs) {
    var f = function () {};
    for (var i = fs.length - 1; i >= 0; i--) {
        f = fs[i].partial(f);
    }
    f();
}

обратного вызова ада можно легко избежать в чистом javascript с закрытием. Решение ниже предполагает, что все обратные вызовы следуют сигнатуре функции(ошибка, данные).

http.createServer(function (req, res) {
  var modeNext, onNext;

  // closure variable to keep track of next-callback-state
  modeNext = 0;

  // next-callback-handler
  onNext = function (error, data) {
    if (error) {
      modeNext = Infinity;
    } else {
      modeNext += 1;
    }
    switch (modeNext) {

    case 0:
      res.writeHead(200, {'Content-Type': 'text/html'});
      var html = "<h1>Demo page</h1>";
      getSomeDate(client, onNext);
      break;

    // handle someData
    case 1:
        html += "<p>"+ data +"</p>";
        getSomeOtherDate(client, onNext);
        break;

    // handle someOtherData
    case 2:
      html += "<p>"+ data +"</p>";
      getMoreData(client, onNext);
      break;

    // handle moreData
    case 3:
      html += "<p>"+ data +"</p>";
      res.write(html);
      res.end();
      break;

    // general catch-all error-handler
    default:
      res.statusCode = 500;
      res.end(error.message + '\n' + error.stack);
    }
  };
  onNext();
});

недавно я создал более простую абстракцию под названием подождать.для для вызова асинхронных функций в режиме синхронизации (на основе волокон). Это на ранней стадии, но работает. Это по адресу:

https://github.com/luciotato/waitfor

используя подождать.для, вы можете вызвать любую стандартную асинхронную функцию nodejs, как если бы это была функция синхронизации.

используя подождать.для ваш код может быть:

var http=require('http');
var wait=require('wait.for');

http.createServer(function(req, res) {
  wait.launchFiber(handleRequest,req, res); //run in a Fiber, keep node spinning
}).listen(8080);


//in a fiber
function handleRequest(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  var someData = wait.for(getSomeDate,client);
  html += "<p>"+ someData +"</p>";
  var someOtherData = wait.for(getSomeOtherDate,client);
  html += "<p>"+ someOtherData +"</p>";
  var moreData = wait.for(getMoreData,client);
  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
};

...или если вы хотите быть менее подробным (а также добавить ловлю ошибок)

//in a fiber
function handleRequest(req, res) {
  try {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(
    "<h1>Demo page</h1>" 
    + "<p>"+ wait.for(getSomeDate,client) +"</p>"
    + "<p>"+ wait.for(getSomeOtherDate,client) +"</p>"
    + "<p>"+ wait.for(getMoreData,client) +"</p>"
    );
    res.end();
  }
  catch(err) {
   res.end('error '+e.message); 
  }

};

во всех случаях getSomeDate, getSomeOtherDate и getMoreData должны быть стандартные асинхронные функции с последним параметром a функция обратного вызова (err,data)

в:

function getMoreData(client, callback){
  db.execute('select moredata from thedata where client_id=?',[client.id],
       ,function(err,data){
          if (err) callback(err);
          callback (null,data);
        });
}

чтобы решить эту проблему, я написал nodent (https://npmjs.org/package/nodent) который незримо предварительно обрабатывает ваш JS. Ваш пример кода станет (async, really-read the docs).

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  someData <<= getSomeDate(client) ;

  html += "<p>"+ someData +"</p>";
  someOtherData <<= getSomeOtherDate(client) ;

  html += "<p>"+ someOtherData +"</p>";
  moreData <<= getMoreData(client) ;

  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
});

очевидно, что есть много других решений, но предварительная обработка имеет то преимущество, что у нее мало или нет накладных расходов во время выполнения, и благодаря поддержке source-map ее легко отлаживать.


у меня была та же проблема. Я видел, как основные библиотеки для узла запускают асинхронные функции, и они представляют собой настолько неестественную цепочку (вам нужно использовать три или более методов confs и т. д.) Для создания вашего кода.

Я потратил несколько недель на разработку решения, чтобы быть простым и легким для чтения. Пожалуйста, попробуйте EnqJS. Все мнения будут оценены.

вместо:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

С EnqJS:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";

  enq(function(){
    var self=this;
    getSomeDate(client, function(someData){
      html += "<p>"+ someData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getSomeOtherDate(client, function(someOtherData){ 
      html += "<p>"+ someOtherData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getMoreData(client, function(moreData) {
      html += "<p>"+ moreData +"</p>";
      self.return();
      res.write(html);
      res.end();
    });
  });
});

обратите внимание, что код быть больше, чем раньше. Но он не вложен, как раньше. Чтобы казаться более естественными, цепи называются сразу:

enq(fn1)(fn2)(fn3)(fn4)(fn4)(...)

и сказать, что он вернулся, внутри функции мы называем:

this.return(response)

Я делаю это довольно примитивным, но эффективным способом. Например. Мне нужно получить модель со своими родителями и детьми, и, скажем, мне нужно сделать для них отдельные запросы:

var getWithParents = function(id, next) {
  var getChildren = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      },
      getParents = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      }
      getModel = function(id, next) {
        /*... code ... */
        if (model) {
          // return next callbacl
          return next.pop()(model, next);
        } else {
          // return last callback
          return next.shift()(null, next);
        }
      }

  return getModel(id, [getParents, getChildren, next]);
}

Использовать Волокна https://github.com/laverdet/node-fibers это делает асинхронный код похожим на синхронный (без блокировки)

Я лично использую эту маленькую обертку http://alexeypetrushin.github.com/synchronize Пример кода из моего проекта (каждый метод на самом деле асинхронный, работает с асинхронным файлом IO) я даже боюсь представить, какой беспорядок это будет с помощью вспомогательных библиотек callback или async-control-flow.

_update: (version, changesBasePath, changes, oldSite) ->
  @log 'updating...'
  @_updateIndex version, changes
  @_updateFiles version, changesBasePath, changes
  @_updateFilesIndexes version, changes
  configChanged = @_updateConfig version, changes
  @_updateModules version, changes, oldSite, configChanged
  @_saveIndex version
  @log "updated to #{version} version"

задач.js предлагает вам:

spawn(function*() {
    try {
        var [foo, bar] = yield join(read("foo.json"),
                                    read("bar.json")).timeout(1000);
        render(foo);
        render(bar);
    } catch (e) {
        console.log("read failed: " + e);
    }
});

вместо этого:

var foo, bar;
var tid = setTimeout(function() { failure(new Error("timed out")) }, 1000);

var xhr1 = makeXHR("foo.json",
                   function(txt) { foo = txt; success() },
                   function(err) { failure() });
var xhr2 = makeXHR("bar.json",
                   function(txt) { bar = txt; success() },
                   function(e) { failure(e) });

function success() {
    if (typeof foo === "string" && typeof bar === "string") {
        cancelTimeout(tid);
        xhr1 = xhr2 = null;
        render(foo);
        render(bar);
    }
}

function failure(e) {
    xhr1 && xhr1.abort();
    xhr1 = null;
    xhr2 && xhr2.abort();
    xhr2 = null;
    console.log("read failed: " + e);
}

после того, как другие ответили, Вы заявили, что ваша проблема-локальные переменные. Кажется, простой способ сделать это-написать одну внешнюю функцию, содержащую эти локальные переменные, затем использовать кучу именованных внутренних функций и получить к ним доступ по имени. Таким образом, вы будете только когда-либо гнездо два глубоко, независимо от того, сколько функций вам нужно связать вместе.

вот попытка моего новичка использовать mysql узел.модуль js с вложенностью:

function with_connection(sql, bindings, cb) {
    pool.getConnection(function(err, conn) {
        if (err) {
            console.log("Error in with_connection (getConnection): " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, function(err, results) {
            if (err) {
                console.log("Error in with_connection (query): " + JSON.stringify(err));
                cb(true);
                return;
            }
            console.log("with_connection results: " + JSON.stringify(results));
            cb(false, results);
        });
    });
}

в ниже приведена перезапись с использованием именованных внутренних функций. Внешняя функция with_connection может использоваться как держатель для локальных переменных. (Вот, у меня есть параметры sql, bindings, cb, которые действуют подобным образом, но вы можете просто определить некоторые дополнительные локальные переменные в with_connection.)

function with_connection(sql, bindings, cb) {

    function getConnectionCb(err, conn) {
        if (err) {
            console.log("Error in with_connection/getConnectionCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, queryCb);
    }

    function queryCb(err, results) {
        if (err) {
            console.log("Error in with_connection/queryCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        cb(false, results);
    }

    pool.getConnection(getConnectionCb);
}

я думал, что, возможно, можно было бы сделать объект с переменными экземпляра, и использовать эти переменные в качестве замены для локальных переменных. Но теперь я нахожу, что вышеуказанный подход с использованием вложенных функций и локальных переменных проще и проще понять. Требуется некоторое время, чтобы разучиться ОО, кажется : -)

Итак, вот моя предыдущая версия с переменными объекта и экземпляра.

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, function(err, results) { self.query(err, results); });
}
DbConnection.prototype.query = function(err, results) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        self.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    self.cb(false, results);
}

function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    pool.getConnection(function (err, conn) { dbc.getConnection(err, conn); });
}

получается, что bind можно использовать с определенной выгодой. Это позволяет мне избавиться от несколько уродливых анонимных функций, которые я создал, которые ничего не сделали, кроме как перенаправить себя на вызов метода. Я не удалось передать метод напрямую, потому что он был бы связан с неправильным значением this. Но с bind, я могу указать значение this что я хочу.

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var f = this.query.bind(this);
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, f);
}
DbConnection.prototype.query = function(err, results) {
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    this.cb(false, results);
}

// Get a connection from the pool, execute `sql` in it
// with the given `bindings`.  Invoke `cb(true)` on error,
// invoke `cb(false, results)` on success.  Here,
// `results` is an array of results from the query.
function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    var f = dbc.getConnection.bind(dbc);
    pool.getConnection(f);
}

конечно, это правильное JS с узлом.JS кодирование - я только что потратил на это пару часов. Но, может быть, с небольшой полировкой эта техника может помочь?


асинхронность.js хорошо работает для этого. Я наткнулся на эту очень полезную статью, которая объясняет необходимость и применению асинхронного.js с примерами:http://www.sebastianseilund.com/nodejs-async-in-practice


Если вы не хотите использовать "шаг" или "seq", попробуйте "line", которая является простой функцией для уменьшения вложенного асинхронного обратного вызова.

https://github.com/kevin0571/node-line


C# - like asyncawait-это еще один способ сделать это

https://github.com/yortus/asyncawait

async(function(){

    var foo = await(bar());
    var foo2 = await(bar2());
    var foo3 = await(bar2());

}

С помощью провод ваш код будет выглядеть так:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});

    var l = new Wire();

    getSomeDate(client, l.branch('someData'));
    getSomeOtherDate(client, l.branch('someOtherData'));
    getMoreData(client, l.branch('moreData'));

    l.success(function(r) {
        res.write("<h1>Demo page</h1>"+
            "<p>"+ r['someData'] +"</p>"+
            "<p>"+ r['someOtherData'] +"</p>"+
            "<p>"+ r['moreData'] +"</p>");
        res.end();
    });
});

для вашего знать рассмотрим Джаз.Яш https://github.com/Javanile/Jazz.js/wiki/Script-showcase


    const jj = require('jazz.js');

    // ultra-compat stack
    jj.script([
        a => ProcessTaskOneCallbackAtEnd(a),
        b => ProcessTaskTwoCallbackAtEnd(b),
        c => ProcessTaskThreeCallbackAtEnd(c),
        d => ProcessTaskFourCallbackAtEnd(d),
        e => ProcessTaskFiveCallbackAtEnd(e),
    ]);