嵌套承诺和拒绝

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌套承诺和拒绝相关的知识,希望对你有一定的参考价值。

我对promise函数有一些问题,我的app有这样的结构: - routes - service - db

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

非常感谢!

答案

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'))会更好

另一答案

嵌套承诺是一种反模式。

参见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的存在。

以上是关于嵌套承诺和拒绝的主要内容,如果未能解决你的问题,请参考以下文章

承诺按顺序运行嵌套承诺并在第一次拒绝时解决

干净的代码和嵌套的承诺 [重复]

链式承诺不会在拒绝时传递

未处理的承诺拒绝条带支付意图示例

CordovaError:承诺被非错误拒绝:错误代码 65

可能未处理的承诺拒绝(id 0)反应原生