【问题标题】:Node.js consecutive method calls with nested callback formatting具有嵌套回调格式的 Node.js 连续方法调用
【发布时间】:2016-09-15 16:12:45
【问题描述】:

所以我是 Node.js 的新手,我只是想知道我的代码设置方式是否有意义。我来自 Java 背景,所以嵌套的回调结构是新的。我有一个 Node 程序,它运行一堆我分解成不同方法的代码。问题是这些方法需要按顺序调用。我的代码现在有这样的结构:

functionOne(data, callback(err) {

   functionTwo(data, callback(err) {

      functionThree(data, callback(err) {

          functionFour(data, callback(err) {
             //Code
          });

      });

   });

});

这是非常简约的,但是这种结构可以吗?使用 Java,我会获取所有方法的返回值,然后将它们传递给下一个函数。根据我目前的理解,我刚才提到的 Java 方法是 Node.js 试图消除的主要内容之一。但无论如何......这个结构看起来还不错吗,这就是它的预期外观吗?只是想确保我一般不会在 Node 上犯任何重大错误。谢谢!

【问题讨论】:

  • 我相信 Promises 是你所需要的。

标签: node.js


【解决方案1】:

你应该尝试使用promises 来避免你的回调地狱,所以它可能是这样的......

const Q = require('q'); // you can do a research for this module.
var myModule = {};
myModule.functionOne = (params) => {
  const deferred = Q.defer(); // wait for this to complete
  // body function
  deferred.resolve(data); // this would be the result of this function
  return deferred.promise; // data is the output on your function
}

myModule.functionTwo = (params) => {
  const deferred = Q.defer(); // wait for this to complete
  // body function
  deferred.resolve(data); // this would be the result of this function
  return deferred.promise; // data is the output on your function
}

myModule.doAll = (params) => {
   myModule.functionOne(params)
   .then((outputFunctionOne) => {
      // this is called after functionOne ends
      return myModule.functionTwo(outputFunctionOne);
   })
   .then((outputFunctionTwo) => {
      // this is called after function 2 ends
      if (outputFunctionTwo.success) {
        // if everything ok, resolve the promise with the final output
        deferred.resolve(outputFunctionTwo);
      } else {
        // reject the promise with an error message
        deferred.reject('error');
      }
   })
   .fail((err) => { 
      // this is call if the promise is rejected or an exception is thrown
      console.log(err); // TODO: Error handling
   })
   .done();
}

module.exports = myModule;

你可以很容易地链接任意数量的 Promise,这样你就可以摆脱回调地狱。最好的部分,你可以在 Javascript 或 Node.js 上做承诺

参考链接https://github.com/kriskowal/q

希望对你有帮助

【讨论】:

  • 回调系统是不是对Node的普遍批评?我知道我看过的所有教程都说“..很棒的是你可以在方法调用中直接传递回调函数!万岁!”,但似乎像我一样一遍又一遍地这样做创建难以理解的代码
  • 是的,基本上如果你链接你的承诺,你可以让你的数据依赖(或函数依赖)对齐到一个单一的级别,而不是需要或读取许多嵌套函数的代码依赖。代码更干净,更易读。我不认为回调地狱只是在节点中,它也可能在 Javascript 中发生。 Promises 是一个很好的解决方法:)
  • “outputFunctionOne”从何而来?是否会使用前一个方法的 return 语句自动传递给下一个 then() 调用?
  • 我已经编辑了我的答案...基本上您在deferred.resolve(data) 中输入的内容就是您在.then 函数中发现的内容...
  • @rpm 回调系统总体上是一件好事。从函数式编程的角度来看,它允许您使用标准库构建异步调用并保持合理的控制流结构。缺点是最终会出现服务器嵌套。 Promise/A 规范旨在缓解这种情况,同时保留将标准库函数链接在一起的能力。它已成为 javascript 中所有异步工作的事实上的标准(鉴于 js 缺少同步阻塞调用,这意味着一切)。
【解决方案2】:

大多数其他答案都将 Promise/A 作为回调问题的答案。这是正确的,并且会为你工作。但是,如果您愿意放弃 javascript 作为您的工作语言,我想给您另一种选择。

介绍 Iced Coffee,CoffeeScript 项目的一个分支。

用冰咖啡你会写:

await functionOne data, defer err
await functionTwo data, defer err2
await functionThree data, defer err3
//etc

然后编译成 CoffeeScript:

functionOne data, (err) ->
   functionTwo data, (err2) ->
      functionThree data, (err3) ->
         //etc

然后编译为您的 Javascript。

functionOne(data, callback(err) {

   functionTwo(data, callback(err2) {

      functionThree(data, callback(err3) {
          //etc    
      });    
   });    
});

【讨论】:

    【解决方案3】:

    如果您使用回调模式,您的代码结构看起来不错。 但是如果你有兴趣让你的代码更清晰和可读,你想在你的异步函数中使用 Promises,所以你可以做这样的事情,而不是向你的函数传递回调:

    function asyncFunction (data){
       return new Promise(function(resolve, reject){
           // Do something with data
           // Here you can call reject(error) to throw an error
           resolve();
       });
    }
    

    你可以调用 Promise 的 then 方法来代替嵌套函数回调。

    asyncFunction(data)
    .then(function(){
        // Promise resolved
        // Something has been done with data
    });
    

    借助 Promises,您还可以并行执行异步函数:

    Promise.all([asyncFunctionA(data), asyncFunctionB(data), asyncFunctionC(data)])
    .then(function(){...});
    

    编辑

    如果您需要将一个函数的值传递给另一个函数,您的代码应如下所示:

    asyncFunctionA(data)
    .then(function(dataA){
        return asyncFunctionB(dataA);
    })
    .then(function(dataB){
        return asyncFunctionC(dataB);
    })
    .then(function(dataC){
    // ...
    });
    

    【讨论】:

    • 所以我可以将“then()”调用串在一起,从而将数据从一种方法传递到另一种方法?
    • 然而,一般来说,现代库在没有回调的情况下调用时会返回 Promise/A 对象。所以你不太可能需要编写这些包装器。
    猜你喜欢
    • 2013-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-27
    • 1970-01-01
    • 2012-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多