这是我的代码:
var thisValue = new models.Value({
id:id,
title:title //this is a unique value
});
console.log(thisValue);
thisValue.save(function(err, product, numberAffected) {
if (err) {
if (err.code === 11000) { //error for dupes
console.error('Duplicate blocked!');
models.Value.find({title:title}, function(err, docs)
{
callback(docs) //this is ugly
});
}
return;
}
console.log('Value saved:', product);
if (callback) {
callback(product);
}
});
如果我检测到正在尝试插入重复项,我会阻止它。但是,当发生这种情况时,我想返回现有文档。正如你所看到的,我已经实现了一串回调,但这是丑陋的,它是不可预测的(即我怎么知道将调用哪个回调?我如何传递正确的回调?)。有谁知道如何解决这个问题?任何帮助表示赞赏。
答案 0 :(得分:18)
虽然您的代码没有处理一些错误情况,并且使用了错误的find
函数,但一般流程通常会给您想要的工作。
findOne
而不是find
因为只有一个结果,因为密钥是唯一的。否则,它将返回一个数组。error
作为第一个参数,你可以直接将回调传递给findOne
函数而不是引入匿名函数。 findOneAndUpdate
,具体取决于您的最终架构和逻辑。如上所述,您可以使用findOneAndUpdate
,但需要支付额外费用。
function save(id, title, callback) {
Value.findOneAndUpdate(
{id: id, title: title}, /* query */
{id: id, title: title}, /* update */
{ upsert: true}, /* create if it doesn't exist */
callback);
}
当然还有一个回调,但是如果找到副本,它会再次写入数据。这是一个问题是否真的取决于用例。
我已经对你的代码进行了一些清理......但它确实很简单,回调应该很清楚。函数的callback
始终接收新保存的文档或匹配为重复的文档。调用saveNewValue
的函数负责检查错误并正确处理错误。您将看到我如何确定无论错误类型如何都会调用回调,并始终以一致的方式调用结果。
function saveNewValue(id, title, callback) {
if (!callback) { throw new Error("callback required"); }
var thisValue = new models.Value({
id:id,
title:title //this is a unique value
});
thisValue.save(function(err, product) {
if (err) {
if (err.code === 11000) { //error for dupes
return models.Value.findOne({title:title}, callback);
}
}
callback(err, product);
});
}
或者,您可以使用promise模式。此示例使用when.js。
var when = require('when');
function saveNewValue(id, title) {
var deferred = when.defer();
var thisValue = new models.Value({
id:id,
title:title //this is a unique value
});
thisValue.save(function(err, product) {
if (err) {
if (err.code === 11000) { //error for dupes
return models.Value.findOne({title:title}, function(err, val) {
if (err) {
return deferred.reject(err);
}
return deferred.resolve(val);
});
}
return deferred.reject(err);
}
return deferred.resolve(product);
});
return deferred.promise;
}
saveNewValue('123', 'my title').then(function(doc) {
// success
}, function(err) {
// failure
});
答案 1 :(得分:10)
我非常喜欢WiredPrairie's answer,但他的承诺实施过于复杂。
所以,我决定添加自己的promise实现。
3.8.x
如果您使用的是最新的Mongoose 3.8.x
,那么就不需要使用任何其他promise模块,因为3.8.0
模型.create()
方法会返回一个承诺:
function saveNewValue(id, title) {
return models.Value.create({
id:id,
title:title //this is a unique value
}).then(null, function(err) {
if (err.code === 11000) {
return models.Value.findOne({title:title}).exec()
} else {
throw err;
}
});
}
saveNewValue('123', 'my title').then(function(doc) {
// success
console.log('success', doc);
}, function(err) {
// failure
console.log('failure', err);
});
models.Value.findOne({title:title}).exec()
也会返回一个承诺,因此不需要回调或任何额外的投射。
如果你通常不在你的代码中使用promises,这里是它的回调版本:
function saveNewValue(id, title, callback) {
models.Value.create({
id:id,
title:title //this is a unique value
}).then(null, function(err) {
if (err.code === 11000) {
return models.Value.findOne({title:title}).exec()
} else {
throw err;
}
}).onResolve(callback);
}
如果您使用3.8.0
之前的任何Mongoose版本,那么您可能需要when
模块的帮助:
var when = require('when'),
nodefn = require('when/node/function');
function saveNewValue(id, title) {
var thisValue = new models.Value({
id:id,
title:title //this is a unique value
});
var promise = nodefn.call(thisValue.save.bind(thisValue));
return promise.spread(function(product, numAffected) {
return product;
}).otherwise(function(err) {
if (err.code === 11000) {
return models.Value.findOne({title:title}).exec()
} else {
throw err;
}
});
}
我正在使用nodefn.call
辅助函数将回调样式的.save()
方法转换为promise。 Mongoose团队承诺在Mongoose 4.x
中添加对它的支持。
然后我使用.spread
辅助方法从.save()
回调中提取第一个参数。