使用 Mongoose FindOne 和 Bluebird Promises 在循环中构建数组
Posted
技术标签:
【中文标题】使用 Mongoose FindOne 和 Bluebird Promises 在循环中构建数组【英文标题】:Building an array in a loop using Mongoose FindOne & Bluebird Promises 【发布时间】:2016-02-15 10:12:39 【问题描述】:我正在使用 Node、Mongoose 和 Bluebird,并且正在开发一个提供多个 RSS 提要的网站。
检索提要时,我想从表中提取已保存到表中的所有文章,并将它们与提要中的任何新文章合并到一个数组中。我似乎找不到等待的方法在返回我的数组之前所有解决的承诺。基本问题是,当loadRSSFeed
被调用时,它基本上会立即调用return articleList
行。如何将所有这些排列起来,以便在我的承诺全部解决之前什么都不返回?
第一个函数只是包装了所有内容:
function loadRSSFeed(rss, newsSource)
var articleList = [];
// Promise.each(rss.channel[0].item, function (article)
rss.channel[0].item.forEach(function (article)
var item = ;
item.link = tryEval(article, "article.link[0]");
Promise(function()
return getArticle(newsSource, article, item, articleList)
.then(function()
return articleList;
)
)
)
;
getArticle 函数如下所示:
function getArticle(newsSource, article, item, articleList)
return Articles.findOne( link: article.link , function (err, doc)
if (doc)
articleList.push(doc._doc);
else
item.title = tryEval(article, "article.title[0]");
item.pubDate = tryEval(article, "article.pubDate[0]");
item.sourceId = newsSource.id;
item.sourceName = newsSource.name;
if (item.pubDate)
try
item.pubDate = new Date(item.pubDate);
catch (err)
item.pubDate = "";
;
item.contentSnippet = tryEval(article, "article.description[0]");
if (item.contentSnippet.indexOf("<") > 0)
item.contentSnippet = item.contentSnippet.substring(0, item.contentSnippet.indexOf("<") - 1);
;
item.image = tryEval(article, "article['media:content'][0].$.url|article.thumbnail[0]");
if (!item.image)
item.image = photoHunt(item);
if (item.image)
item.contentSnippet = "";
;
if (item.title && item.link && (item.image || item.contentSnippet))
articleList.push(saveArticle(item));
)
saveArticle 函数如下所示:
function saveArticle(article)
var curArticle = ;
if (article._id)
curArticle = article;
curArticle._id = article._id;
curArticle.isNew = false;
else
curArticle = new Articles();
curArticle.title = article.title;
curArticle.link = article.link;
curArticle.pubDate = article.pubDate;
curArticle.image = article.image;
curArticle.contentSnippet = article.contentSnippet;
curArticle.sourceName = article.name;
curArticle.sourceId = article.sourceId;
if (article.haters)
curArticle.haters = article.haters;
;
if (article.lovers)
curArticle.lovers = article.lovers;
;
if (article.readers)
curArticle.readers = article.readers;
;
curArticle.save(function (err)
if (err)
console.log(err);
);
return curArticle;
;
这就是 Articles 模型的样子,以防万一这里出现问题:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
// NewsSchema = new Schema( name: String );
var ArticlesSchema = new Schema(
title: String,
link: String,
pubDate: Date,
image: String,
contentSnippet: String,
sourceName: String,
lovers: [],
haters: [],
readers: [],
forumLinks: []
);
module.exports = mongoose.model('Articles', ArticlesSchema);
【问题讨论】:
【参考方案1】:在我看来,您可以使用 Bluebird 的 Promise.map()
来遍历所有 RSS 提要并等待所有提要完成:
function loadRSSFeed(rss, newsSource)
// Promise.each(rss.channel[0].item, function (article)
return Promise.map(rss.channel[0].item, function(article)
var item = ;
item.link = tryEval(article, "article.link[0]");
return getArticle(newsSource, article, item, articleList);
).then(function(articleList)
// filter out any empty items
return articleList.filter(function(article)
return !!article;
);
);
function getArticle(newsSource, article, item)
var find = Promise.promisify(Articles.findOne, context: Articles);
return find(link: article.link).then(doc)
if (doc)
return doc._doc;
else
item.title = tryEval(article, "article.title[0]");
item.pubDate = tryEval(article, "article.pubDate[0]");
item.sourceId = newsSource.id;
item.sourceName = newsSource.name;
if (item.pubDate)
try
item.pubDate = new Date(item.pubDate);
catch (err)
item.pubDate = "";
;
item.contentSnippet = tryEval(article, "article.description[0]");
if (item.contentSnippet.indexOf("<") > 0)
item.contentSnippet = item.contentSnippet.substring(0, item.contentSnippet.indexOf("<") - 1);
;
item.image = tryEval(article, "article['media:content'][0].$.url|article.thumbnail[0]");
if (!item.image)
item.image = photoHunt(item);
if (item.image)
item.contentSnippet = "";
;
if (item.title && item.link && (item.image || item.contentSnippet))
return saveArticle(item);
// unsure what your code intends if there was no article here
// this will return undefined which will get filtered out later
)
function saveArticle(article)
return new Promise(function(resolve, reject)
var curArticle;
if (article._id)
curArticle = article;
curArticle._id = article._id; // don't know why this is needed since curArticle === article already
curArticle.isNew = false;
else
curArticle = new Articles();
curArticle.title = article.title;
curArticle.link = article.link;
curArticle.pubDate = article.pubDate;
curArticle.image = article.image;
curArticle.contentSnippet = article.contentSnippet;
curArticle.sourceName = article.name;
curArticle.sourceId = article.sourceId;
if (article.haters)
curArticle.haters = article.haters;
;
if (article.lovers)
curArticle.lovers = article.lovers;
;
if (article.readers)
curArticle.readers = article.readers;
;
curArticle.save(function (err)
if (err) reject(err) else resolve(curArticle);
);
);
;
【讨论】:
谢谢!超级接近,如果我没看错的话,articleList 将在调用函数中返回,因为似乎是未解决的承诺,并且不再保存新文章 - 我认为是因为承诺被搁置了。 @MikeFeltman - 我错过了关于saveArticle()
异步的部分(我没有看到)。您是否也需要我帮助解决这个问题,或者您可以将这些相同的原则应用于该功能吗?
让我试一试,如果没有,我会回来的。 :) 再次感谢!我不确定这是唯一的问题,因为有些文章已经在表格中,而且它们也不在数组中。也许不是,我想在所有的承诺都得到解决之前,真的不可能有一个数组。
@MikeFeltman - 我重新编写了答案以合并saveArticle()
。
非常感谢我所拥有的。调用 promisified find 时出现错误。这是一个未处理的拒绝 TypeError: Invalid select() 参数。在 Query.select 中必须是字符串或对象。我追踪了它,它在猫鼬的内部。没有指定投影似乎是一个问题,这应该是可选的。以上是关于使用 Mongoose FindOne 和 Bluebird Promises 在循环中构建数组的主要内容,如果未能解决你的问题,请参考以下文章
承诺问题:使用 Q.nfcall() 调用 mongoose.findOne()
Mongoose `findOne` 操作在 10000 毫秒后超时
更新 findOne()/find()/findById() 返回的文档 - mongoose