【问题标题】:Is there a better way to handle nested promises in NodeJS?有没有更好的方法来处理 NodeJS 中的嵌套承诺?
【发布时间】:2018-07-19 15:07:15
【问题描述】:

我的代码看起来类似于:

const MyClass {
  checkExists: function(db_client) {
    return new Promise(fulfill, reject) {
      var sql = 'select * from table';
      db_client.connect().then(c => {
      }).then(res => {
        client.release();
        fulfill(res.rows[0].col1 === 1 ? true : false);
      }).catch(error => {
        reject(error);
      });
    });
  }
  doSomething: function(db_client) {
    return new Promise(fulfill, reject) {
      var sql = 'delete from table where x=1';
      db_client.connect().then(c => {     
      }).then(res => {
        fulfill();
      }).catch(error => {
        reject(error);
      });
    });
  }
};

module.exports = MyClass;

var myc = require('./MyClass.js');

myc.checkExists(db_client).then(resp => {
  if(resp === true) {
    myc.doSomething(db_client).then(resp => {
    console.log('success.');
  } else {
    console.log('we are done.');
  }
  }).catch(error => {
    console.log(error);
  });
}).catch(error => {
  console.log(error);
});

根据上面的示例,我必须运行一个依赖于另一个查询结果的查询。 (是伪代码,如有错误请见谅)

但是,我注意到它开始导致嵌套的 Promise 或函数调用,其中包含来自另一个 Promise 的实现中的 Promise。

我可以看到这种情况越来越糟。这是犹太洁食吗?有没有更好的方法来思考/处理我正在尝试做的事情?

编辑:

不知道为什么它被标记为一个问题的重复,其中发布者似乎明确意识到反模式并询问如何避免它与反模式/解决方案不知道的问题张贴者,但认识到编程风格中的问题并正在寻求帮助(其中的讨论可能会产生相同类型的解决方案)。

【问题讨论】:

标签: javascript node.js promise


【解决方案1】:

如果 .then 中的函数返回一个值,那么 .then() 的结果将成为解析为该值的 Promise。使用这个原理,我们可以重写你的一个方法。

function doSomething(db_client) {
  var sql = 'delete from table where x=1';
  return db_client.connect().then(() => undefined);
}

如果你想返回连接的值,那就更简单了

function doSomething(db_client) {
  var sql = 'delete from table where x=1';
  return db_client.connect();
}

【讨论】:

  • 我是否有机会澄清否决票的原因?
【解决方案2】:

有没有更好的方法来处理 NodeJS 中的嵌套承诺?

是的,有。您可以进行很多改进。您的伪代码似乎缺少一些部分,但我将展示最简单的版本,以捕捉您所做工作的精神:

const MyClass {
    checkExists: function(db_client) {
        var sql = 'select * from table';
        return db_client.connect().then(c => {
            // missing code here in your pseudo code
            // do something with c here to create the variable res
            client.release();
            // make this be the resolved value of the returned promise
            return res.rows[0].col1 === 1;
        });
    }
    doSomething: function(db_client) {
        var sql = 'delete from table where x=1';
        return db_client.connect().then(c => {
            // do something with c here
            // return value or another promise
        });
    }
  };

  module.exports = MyClass;

  var myc = require('./MyClass.js');

  myc.checkExists(db_client).then(resp => {
    if(resp === true) {
        return myc.doSomething(db_client).then(resp => {
            console.log('success.');
            // return something here to be the resolved value of the promise
            return resp;
        });
    } else {
        console.log('we are done.');
        // return something here to be the resolved value of the promise
        return resp;
    }
  }).catch(error => {
    console.log(error);
  });

以下是基本概念:

  1. 不要用手动创建的 Promise 包装现有的 Promise。这被称为反模式,它没有提供任何好处,使您的代码膨胀并且是错误处理错误的机会。只是回报你已经拥有的承诺。

  2. 通过从.then() 处理程序返回承诺,从.then() 处理程序中链接嵌入承诺。

  3. 您不需要在每个级别都.catch(),除非您明确需要在较低级别执行某些操作。被拒绝的承诺会自动向上传播到下一个.catch()。这可以大大简化错误处理。

  4. 如果你需要一个较低级别的.catch(),即使只是为了记录目的,那么你必须throw一个来自.catch()的错误来保持承诺被拒绝,否则拒绝将被视为“已处理”,链将切换到已解决。

  5. .then() 处理程序中,您有三个选项。您可以返回另一个承诺,它将被添加到链中。您可以返回一个值,它将成为父 Promise 链的已实现值。您可以抛出异常,当前链将被拒绝。如果什么都不返回,那么父 Promise 链的已实现值将变为 undefined(就像一个什么都不返回的函数一样)。

仅供参考,这里有几篇关于 Promise 反模式的文章,值得研究和实践:

Six Promise Anti-Patterns

Deferred and .then(success, fail) anti-patterns

关于您的代码的其他说明:

  1. 在某些地方,您似乎缺少释放数据库连接的代码。

  2. 在设计时,您应该非常清楚仅处理自己的操作(包括错误)的函数,并且不期望调用者会处理错误而不是其工作的函数执行操作并返回结果或错误,以便调用者可以决定如何处理它们。这决定了您是否只是让被拒绝的承诺返回(供调用者处理),或者您是否必须在这里.catch() 他们并对其进行处理。

  3. 1234563 .

【讨论】:

  • 为什么投反对票?这不是教给与 OP 正在做什么相关的各种事情吗?此外,没有任何解释的否决票对社区没有多大好处,因为作者没有得到关于如何改进他们的答案的具体反馈。请在您投反对票时发表评论并说明原因。否决票的最佳结果是作者改进了他们的答案。当您开车投反对票时,没有机会获得最佳结果。
  • 成功!我现在明白了很多。我完全以不同的方式思考这个问题,这让我明白了。
  • @jfriend00 我不会质疑您答案的质量,但是,我发现您的答案存在于这个问题上,特别是使它没有用处。如果将其展示在其中一个更受欢迎的副本上,那将更加有用。
  • @KevinB - 请指点我一个副本,它教给 OP 代码的所有错误以及所有看起来他们需要学习的东西。您可能会找到四个重复项,这些重复项可能涵盖此处的所有内容,但对 OP 的代码并不十分具体,但这对任何人来说会更好吗?如果您将某些内容标记为 dup,而该 dup 仅涵盖完整答案可能涵盖的部分内容,则堆栈溢出不会更好。我只会在重复内容涵盖答案中需要教授的所有内容时才标记重复内容。而且,相信我,我标记了数百个重复。
【解决方案3】:

如果你从 then() 中返回一个 Promise,根 Promise 对象的下一个 then() 将获得之前的 Promise 的结果。示例

Promise.resolve(42).then(function(answer) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve('Answer to life = ' + answer);
    }, 3000);
  })
}).then(function(str){
    document.write(str);
});

【讨论】:

    猜你喜欢
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    • 1970-01-01
    相关资源
    最近更新 更多