我使用的是猫鼬的一些数据插入到MongoDB的。 代码如下:
var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/test');
var conn = mongoose.connection;
// insert users
conn.collection('users').insert([{/*user1*/},{/*user2*/}], function(err, docs) {
var user1 = docs[0], user2 = docs[1];
// insert channels
conn.collection('channels').insert([{userId:user1._id},{userId:user2._id}], function(err, docs) {
var channel1 = docs[0], channel2 = docs[1];
// insert articles
conn.collection('articles').insert([{userId:user1._id,channelId:channel1._id},{}], function(err, docs) {
var article1 = docs[0], article2 = docs[1];
}
});
};
你可以看到有很多的嵌套回调的出现,所以我试图用q来重构它。
我希望代码如下:
Q.fcall(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
// Do something with value4
}, function (error) {
// Handle any error from step1 through step4
})
.end();
但我不知道该怎么做。
Answer 1:
你要使用Q.nfcall
,记录在README和维基。 所有的猫鼬的方法是节点式。 我也将使用.spread
而不是手动解构.then
。
var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/test');
var conn = mongoose.connection;
var users = conn.collection('users');
var channels = conn.collection('channels');
var articles = conn.collection('articles');
function getInsertedArticles() {
return Q.nfcall(users.insert.bind(users), [{/*user1*/},{/*user2*/}]).spread(function (user1, user2) {
return Q.nfcall(channels.insert.bind(channels), [{userId:user1._id},{userId:user2._id}]).spread(function (channel1, channel2) {
return Q.nfcall(articles.insert.bind(articles), [{userId:user1._id,channelId:channel1._id},{}]);
});
})
}
getInsertedArticles()
.spread(function (article1, article2) {
// you only get here if all three of the above steps succeeded
})
.fail(function (error) {
// you get here if any of the above three steps failed
}
);
在实践中,你很少会想使用.spread
,因为你通常会插入一个数组,你不知道的大小。 在这种情况下,代码可以看起来更喜欢这个 (我在这里也说明Q.nbind
)。
为了比较与原来的不公平的比较,因为你原来有没有错误处理。 原来的修正节点式的版本会像这样:
var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/test');
var conn = mongoose.connection;
function getInsertedArticles(cb) {
// insert users
conn.collection('users').insert([{/*user1*/},{/*user2*/}], function(err, docs) {
if (err) {
cb(err);
return;
}
var user1 = docs[0], user2 = docs[1];
// insert channels
conn.collection('channels').insert([{userId:user1._id},{userId:user2._id}], function(err, docs) {
if (err) {
cb(err);
return;
}
var channel1 = docs[0], channel2 = docs[1];
// insert articles
conn.collection('articles').insert([{userId:user1._id,channelId:channel1._id},{}], function(err, docs) {
if (err) {
cb(err);
return;
}
var article1 = docs[0], article2 = docs[1];
cb(null, [article1, article2]);
}
});
};
}
getInsertedArticles(function (err, articles) {
if (err) {
// you get here if any of the three steps failed.
// `articles` is `undefined`.
} else {
// you get here if all three succeeded.
// `err` is null.
}
});
Answer 2:
随着替代延迟承诺的实现,你可以做如下:
var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/test');
var conn = mongoose.connection;
// Setup 'pinsert', promise version of 'insert' method
var promisify = require('deferred').promisify
mongoose.Collection.prototype.pinsert = promisify(mongoose.Collection.prototype.insert);
var user1, user2;
// insert users
conn.collection('users').pinsert([{/*user1*/},{/*user2*/}])
// insert channels
.then(function (users) {
user1 = users[0]; user2 = users[1];
return conn.collection('channels').pinsert([{userId:user1._id},{userId:user2._id}]);
})
// insert articles
.match(function (channel1, channel2) {
return conn.collection('articles').pinsert([{userId:user1._id,channelId:channel1._id},{}]);
})
.done(function (articles) {
// Do something with articles
}, function (err) {
// Handle any error that might have occurred on the way
});
Answer 3:
考虑Model.save代替Collection.insert (在我们的情况完全一样)。
你并不需要使用Q ,你可以用自己的保存方法,并直接返回一个猫鼬的承诺 。
首先创建一个实用的方法来包装保存功能,这不是很干净,但喜欢的事:
//Utility function (put it in a better place)
var saveInPromise = function (model) {
var promise = new mongoose.Promise();
model.save(function (err, result) {
promise.resolve(err, result);
});
return promise;
}
然后你可以使用它,而不是保存到您的链条承诺
var User = mongoose.model('User');
var Channel = mongoose.model('Channel');
var Article = mongoose.model('Article');
//Step 1
var user = new User({data: 'value'});
saveInPromise(user).then(function () {
//Step 2
var channel = new Channel({user: user.id})
return saveInPromise(channel);
}).then(function (channel) {
//Step 3
var article = new Article({channel: channel.id})
return saveInPromise(article);
}, function (err) {
//A single place to handle your errors
});
我想这是一种我们正在寻找简单的..对不对? 当然,效用函数可以与猫鼬更好的整合来实现。
让我知道你的想法有关。
顺便说有关于猫鼬Github上,准确的问题一个问题:
我希望它会很快得到解决。 我认为这需要一些时间,因为他们都在思考的开关从mpromise到Q :请参见这里 ,然后在这里 。
Answer 4:
两年后,这个问题只是突然在我的RSS客户端...
自2012年5月的事情有所感动的,我们可能会选择现在来解决这一以不同的方式。 更具体地讲,JavaScript社区已成为“降低感知”既然决定包括Array.prototype.reduce
在ECMAScript5(和其他阵列方法)。 Array.prototype.reduce
总是(现在仍然是)可以作为填充工具,但我们许多人在当时是很少赞赏。 那些谁是曲线的跑在前面可以提出异议在这一点上,当然。
在这个问题所带来的问题似乎是公式化,与规则如下:
- 作为第一个参数,以传递的数组中的对象
conn.collection(table).insert()
建立如下(其中N
对应于该对象的索引中的阵列): - [{},...]
- [{用户名:userN._id},...]
- [{用户名:userN._id,的channelID:channelN._id},...]
- 表名(按顺序)为:
users
, channels
, articles
。 - 所述corresopnding对象属性是:
user
, channel
, article
(即没有表名的复数化的')。
从通用的模式本文由Taoofcode )作出异步调用的系列:
function workMyCollection(arr) {
return arr.reduce(function(promise, item) {
return promise.then(function(result) {
return doSomethingAsyncWithResult(item, result);
});
}, q());
}
有了相当光适应,这种模式可以进行编排所需的排序:
function cascadeInsert(tables, n) {
/*
/* tables: array of unpluralisd table names
/* n: number of users to insert.
/* returns promise of completion|error
*/
var ids = []; // this outer array is available to the inner functions (to be read and written to).
for(var i=0; i<n; i++) { ids.push({}); } //initialize the ids array with n plain objects.
return tables.reduce(function (promise, t) {
return promise.then(function (docs) {
for(var i=0; i<ids.length; i++) {
if(!docs[i]) throw (new Error(t + ": returned documents list does not match the request"));//or simply `continue;` to be error tolerant (if acceptable server-side).
ids[i][t+'Id'] = docs[i]._id; //progressively add properties to the `ids` objects
}
return insert(ids, t + 's');
});
}, Q());
}
最后,这里的承诺,回国工作器功能, insert()
function insert(ids, t) {
/*
/* ids: array of plain objects with properties as defined by the rules
/* t: table name.
/* returns promise of docs
*/
var dfrd = Q.defer();
conn.collection(t).insert(ids, function(err, docs) {
(err) ? dfrd.reject(err) : dfrd.resolve(docs);
});
return dfrd.promise;
}
因此,你可以指定为传递给参数cascadeInsert
,实际的表/属性名称和用户数量的接入。
cascadeInsert( ['user', 'channel', 'article'], 2 ).then(function () {
// you get here if everything was successful
}).catch(function (err) {
// you get here if anything failed
});
这很好地工作,因为在问题的表中的所有具有规则复数(用户=>用户,信道=>信道)。 如果其中任何一个不规则(例如,刺激=>刺激,孩子=>儿),那么我们就需要重新思考 - (可能实现查找散)。 在任何情况下,调整将是相当琐碎。
Answer 5:
今天,我们有猫鼬-Q为好。 一个插件,猫鼬,让你的东西像execQ和saveQ其返回Q承诺。
文章来源: How to use “q” module for refactoring mongoose code?