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

时间:2018-02-08 22:24:06

标签: javascript node.js promise

我的代码类似于:

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

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

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

我可以看到这变得越来越糟。这是犹太人吗?是否有更好的方法来思考/处理我正在尝试做的事情?

编辑:

不确定为什么它被标记为问题的副本,其中海报似乎明确地意识到反模式并且正在询问如何避免它而不是反模式/解决方案未知的问题海报,但认识到编程风格的问题,并寻求帮助(讨论可能导致相同类型的解决方案)。

3 个答案:

答案 0 :(得分:4)

  

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

是的,有。你可以做很多改进。您的伪代码似乎缺少一些部分,但我会展示最简单的版本,它可以捕捉到您正在做的事情的精神:

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. 不要用手动创建的承诺包装现有的承诺。这被称为反模式,它没有任何好处,可以代表你的代码并且是错误处理错误的机会。只要回复你已经拥有的承诺。

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

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

  4. 如果您需要较低级别的.catch(),即使只是为了记录目的,那么您必须throw .catch()发出错误以保证承诺被拒绝,否则拒绝将被视为"处理"并且链将切换为已解决。

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

  6. 仅供参考,这里有几篇关于承诺反模式的文章,值得研究并付诸实践:

    Six Promise Anti-Patterns

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

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

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

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

    3. 如果.catch()要清理或记录或类似的东西,但希望调用者仍然看到错误,则必须重新抛出错误,以便在{{{{{ 1}}运行。

答案 1 :(得分:-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 :(得分:-1)

如果从then()中返回一个promise,那么root promise对象的next()将获得前一个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);
});