嵌套的承诺&拒绝

时间:2018-03-01 19:26:52

标签: node.js mongodb promise

我对promise函数有一些问题,我的app有这样的结构: - 路线 - 服务 - db

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

  

无法读取未定义的属性'然后'

错误在哪里?

这是我的资源:

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)
        }
      });
    });
  }
非常感谢!

2 个答案:

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

会更好

答案 1 :(得分:-1)

嵌套承诺是一种反模式。

请参阅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