我有一个Node 8 / Express 4 / Mongoose 4 API,并希望概括一些代码,以便我可以将其重用于其他部分。
考虑以下用于创建新用户的代码:
function postUser(req, res, next) {
var body = req.body;
if ("data" in body) {
var user = new User(body.data);
user.save(function(err, savedUser) {
if (err) {
if (err.name === 'MongoError' && err.code === 11000) {
// user already exists
res.status(400).json({status: "fail", message: "User already exists"});
} else {
return next(err);
}
} else {
// user successfully saved
res.json({status: "success", data: savedUser});
}
});
} else {
// malformed body
res.status(400).json({status: "fail", message: "Malformed body"});
}
}
让我们假设我有其他功能可以做类似的工作,其中一些是回调地狱。我怎样才能最好地概括上面的代码?我想过使用像这样的promise-chains:
function postUser(req, res, next) {
validateBody(req.body)
.then(createNewUser)
.then(user => sendUser(user, res))
.catch(e => handleErrors(e, res));
}
function validateBody(body) {
return new Promise(function(resolve, reject) {
if ("data" in body) {
resolve(body.data);
} else {
reject(new InvalidBodyError());
}
});
}
function createNewUser(userObj) {
return new Promise(function(resolve, reject) {
var user = new User(userObj);
user.save(function(err, savedUser) {
if (err) {
if (err.name === 'MongoError' && err.code === 11000) {
// user already exists
reject(new UserAlreadyExistsError(userObj));
} else {
// other error
reject(err);
}
} else {
// user successfully saved
resolve(savedUser);
}
});
});
}
function handleErrors(e, res) {
if (e instanceof InvalidObjectIdError) handleInvalidObjectIdError(e, res)
else if (e instanceof UserNotFoundError) handleUserNotFoundError(e, res)
else if (e instanceof InvalidBodyError) handleInvalidBodyError(e, res)
else if (e instanceof UserAlreadyExistsError) handleUserAlreadyExistsError(e, res)
// TODO: handle unknown errors
}
正如您所看到的,它看起来更干净,更可重复使用。但它如何在负载下执行?我特别担心每个请求创建多个promise。这是否规模?
解决它的另一种方法是创建一个通用的基类来解决泛型的东西,然后用特定于实现的方法(伪代码)扩展这个类:
class Action {
constructor() {}
postDoc(Base, req, res, next) {
var body = req.body;
if ("data" in body) {
var doc= new Base(body.data);
doc.save(function(err, savedDoc) {
if (err) {
if (err.name === 'MongoError' && err.code === 11000) {
// docalready exists
res.status(400).json({status: "fail", message: "Doc already exists"});
} else {
return next(err);
}
} else {
// user successfully saved
res.json({status: "success", data: savedDoc});
}
});
} else {
// malformed body
res.status(400).json({status: "fail", message: "Malformed body"});
}
}
}
class UserAction extends Action {
constructor() {
super();
}
postUser(body, req, res, next) {
this.postDoc(User, req, res, next);
}
}
class AnotherAction extends Action {
constructor() {
super();
}
postAnother(body, req, res, next) {
this.postDoc(AnotherBase, req, res, next);
}
}
然后只使用UserAction或AnotherAction(在我的情况下,User是一个猫鼬模型)。 你更喜欢哪一个?
答案 0 :(得分:0)
我想过使用像这样的promise-chains,它更干净,更可重用。但它如何在负载下执行?
很好。
我特别关注为每个请求创建多个承诺。这是否规模?
是。承诺很便宜。看看你为每个请求创建了多少其他对象和回调闭包 - 它完全相同。
但是,您可以进一步简化:
function validateBody(body) {
return "data" in body
? Promise.resolve(body.data)
: Promise.reject(new InvalidBodyError());
}
function createNewUser(userObj) {
return new Promise(function(resolve, reject) {
new User(userObj).save(function(err, savedUser) {
if (err) reject(err);
else resolve(savedUser);
});
}).catch((err) => {
if (err.name === 'MongoError' && err.code === 11000) {
// user already exists
throw new UserAlreadyExistsError(userObj);
} else {
// other error
throw err;
});
});
}
解决它的另一种方法是创建一个通用的基类来解决泛型的东西,然后用特定于实现的方法扩展这个类
不,不要这样做。继承是错误的工具。创建像postDoc
这样的通用辅助函数,它可以对要创建的文档类型进行抽象,这是一个好主意,但没有充分的理由将它们放在class
es中。将它们与承诺结合起来。如果辅助函数的参数数量失控,您可以使用对象,甚至是class
,但不要使用继承 - 请改用不同的实例。例如,代码可能如下所示:
const userAction = new Action(User, UserAlreadyExistsError);
const anotherAction = new Action(AnotherBase, …);
function post(action) {
return (req, res, next) => {
action.postDoc(req)
.then(doc => send(doc, res))
.catch(e => handleErrors(e, res));
};
}