【问题标题】:nested Promise & reject嵌套的承诺和拒绝
【发布时间】:2018-03-01 19:26:52
【问题描述】:

我的 Promise 函数有一些问题,我的应用程序有这样的结构: - 路线 - 服务 -分贝

db 是一个在应用程序启动时初始化的类,我在其中为 insert/find/ecc 创建了一些包装函数...,service 是路由和 db 之间的一层,我在这里完成了大部分工作。我的问题是,使用下面的代码,如果用户已经存在,我想抛出错误或拒绝承诺,但是当我尝试做这样的事情时,我得到了

无法读取未定义的属性“then”

哪里出错了?

这是我的资源:

router.put('/', (req, res, next) => {
  bcrypt.hash(req.body.password, 10)
    .then(function (hash) {
      req.body.password = hash;
      service.addUser(req.body)
        .then((user) => {
          return res.json(user);
        })
        .catch((err) => {
          return res.json(err);
        });
    })
    .catch((err) => {
      return res.json(err);
    });
});

这是服务:

  getBy(query) {
    return this.mongo.find(query);
  }

  addUser(data) {

    if(!data.email) {
      return Promise.reject('email_missing');
    }

    const self = this;
    self.getBy({ email: data.email })
      .then((user) => {
        if(user.length) {
          return Promise.reject('user_exist');
        }
        return self.mongo.insert(data)
      })
      .catch((err) => {
        return Promise.reject(err);
      });
  }

这是数据库连接:

  find(query) {
    const self = this;
    return new Promise((resolve, reject) => {
      self.collection.find(query).toArray((err, res) => {
        if (err) {
          self.logger.info('Mongo::find error', err);
          reject(err);
        } else {
          self.logger.info('Mongo::find', query);
          resolve(res);
        }
      });
    });
  }

  insert(data) {
    const self = this;
    return new Promise((resolve, reject) => {
      self.collection.insert(data, (err, res) => {
        if (err) {
          self.logger.info('Mongo::insert error', err);
          reject (err)
        } else {
          self.logger.info('Mongo::insert', res);
          resolve(res)
        }
      });
    });
  }

非常感谢!

【问题讨论】:

    标签: node.js mongodb promise


    【解决方案1】:

    addUser 函数不返回 Promise。代码应如下所示:

    addUser(data) {
    
      if (!data.email) {
        return Promise.reject('email_missing');
      }
    
      const self = this;
      return self.getBy({
          email: data.email
        })
        .then((user) => {
          if (user.length) {
            return Promise.reject('user_exist');
          }
          return self.mongo.insert(data)
        })
        .catch((err) => {
          return Promise.reject(err);
        });
    }
    

    这里的.catch 块没有意义,因为它只包含return Promise.reject(err),因此您可以将其删除:

    addUser(data) {
    
      if (!data.email) {
        return Promise.reject('email_missing');
      }
    
      const self = this;
      return self.getBy({
          email: data.email
        })
        .then((user) => {
          if (user.length) {
            return Promise.reject('user_exist');
          }
          return self.mongo.insert(data)
        });
    }
    

    在路由器中,您还必须在 .then 中返回 Promise,您可以删除一个 .catch 块:

    router.put('/', (req, res, next) => {
      bcrypt.hash(req.body.password, 10)
        .then(function(hash) {
          req.body.password = hash;
          return service.addUser(req.body) // return the Promise ehre
        })
        // the then can be move out here, to avoid nesting
        .then((user) => {
          return res.json(user);
        })
        // only on catch is required
        .catch((err) => {
          return res.json(err);
        });
    });
    

    附加说明,您应该始终以真正的错误拒绝。所以最好写,Promise.reject(new Error('user_exist'))

    【讨论】:

      【解决方案2】:

      嵌套 promise 是一种反模式。

      见Promise Patterns & Anti-Patterns中的#2

      它被认为是一种反模式,因为它降低了可理解性,并使调用堆栈过于复杂,使调试(更多)成为一场噩梦。

      所以而不是:

      bcrypt.hash(req.body.password, 10)
          .then(function (hash) {
            req.body.password = hash;
            service.addUser(req.body) // ANTI-PATTERN
              .then((user) => {
                return res.json(user); // [1]
              })
              .catch((err) => {
                return res.json(err); // [2]
              });
          })
          .catch((err) => {
            return res.json(err);
          });
      });
      

      改为这样做:

      const SALT_ROUNDS = 10
      
      app.get(URL, function(req, res) {
      
        function setHashedPassword() {
          // TODO first test for existence of password in req.body
          return bcrypt.hash(req.body.password, SALT_ROUNDS)
            .then(hash => req.body.password = hash)
        }
      
        function addUser() {
          return service.addUser(req.body)
        }
      
        Promise.all([ setHashedPassword(), addUser() ])
          .then((results) => {
            const user = results[1]
            res.json(user)
          })
          .catch((err) => {
            res.json(err)
          })
      })
      

      请注意,在 OP 代码中的 [1] 和 [2] 处,return 没有任何意义,因为没有可以返回值的活动上下文。

      我也会用如下对象回应:

      res.json({ok:true, user:user})
      

      和

      res.json({ok:false, error:err})
      

      因此您可以在客户端检查成功或失败。

      是的,我知道您可能认为 ok 在这里是多余的,但最好对单个结果值进行标准化,这样您就不必在检查之前先测试 error 是否存在user 的存在。

      【讨论】:

      • 我很想知道为什么这被否决了?
      猜你喜欢
      • 2021-03-14
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      • 2019-11-25
      • 2017-09-04
      • 2017-04-20
      • 1970-01-01
      • 2013-09-16
      相关资源
      最近更新 更多