我对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
的存在。