我正在使用Node.js / Express编写Web服务,它根据模板生成一些对象,然后返回生成的数据。我正在使用Bluebird承诺管理所有异步逻辑。在删除所有不重要的东西后,我的代码看起来像这样[1]。
我的问题是,如果请求的输出元素数量很大,核心逻辑可能会阻塞几秒钟。由于我一直在玩这个项目的ES6,我的第一个想法是将元素创建分解为生成器[2]。但是,我能找到从这个生成器得到所有结果的唯一方法是Array.from
,这对阻塞没有帮助。
我玩过.map
,.all
,.coroutine
以及其他一些事情,试图异步收集发电机的结果,但我还没有好运。使用Bluebird有什么好办法吗? (或者也许是完全这样做的更好方法?)
Native ES6 Promise.all可以使用迭代器并返回一组值,但是V8 doesn't support this yet。此外,在我对polyfills / Firefox的实验中,它似乎是同步的。
这不是太常见的操作,所以我不太关心绝对性能。我只是想避免阻塞事件队列,我更喜欢一个简单易读的维护解决方案。
[1]:
let Bluebird = require('bluebird');
let templates = ...; // logic to load data templates
function createRandomElementFromRandomTemplate(templates) {
let el;
// synchronous work that can take a couple of milliseconds...
return el;
};
api.createRandomElements = function(req, res) {
let numEls = req.params.numEls;
Bluebird.resolve(templates)
.then(templates => {
let elements = [];
// numEls could potentially be several thousand
for(let i = 0; i < numEls; ++i) {
elements.push(createRandomElementFromRandomTemplate(templates));
}
return elements;
})
.then(elements => {
res.json(elements);
})
.error(err => {
res.status(500).json(err);
});
}
[2]:
function* generateRandomElementsFromRandomTemplate(templates, numEls) {
for(let i = 0; i < numEls; ++i) {
let el;
// synchronous work that can take a couple of milliseconds...
yield el;
}
}
api.createRandomElements = function(req, res) {
let numEls = req.params.numEls;
Bluebird.resolve(templates)
.then(templates => {
// this still blocks
return Array.from(generateRandomElementsFromRandomTemplate(templates, numEls));
})
.then(elements => {
res.json(elements);
})
.error(err => {
res.status(500).json(err);
});
}
答案 0 :(得分:2)
在本杰明建议的情况下,仔细观察蓝鸟的.map()
后,我发现这是一个不错的解决方案。不过,我仍然觉得我错过了一些东西。
我开始使用Bluebird的主要原因是因为Mongoose,所以我留下了一点,以获得更逼真的样本。
let Bluebird = require('bluebird');
let mongoose = require('mongoose');
Bluebird.promisifyAll(mongoose);
const Template = mongoose.models.Template,
UserPref = mongoose.models.UserPref;
// just a normal function that generates one element with a random choice of template
function createRandomElementFromRandomTemplate(templates, userPrefs) {
let el;
// synchronous work that can take a couple of milliseconds...
return el;
}
api.generate = function(req, res) {
let userId = req.params.userId;
let numRecord = req.params.numRecords
let data;
Bluebird.props({
userprefs: UserPref.findOneAsync({userId: userId}),
templates: Template.findAsync({})
})
.then(_data => {
data = _data;
// use a sparse array to convince .map() to loop the desired number of times
return Array(numRecords);
})
.map(() => {
// ignore the parameter map passes in - we're using the exact same data in each iteration
// generate one item each time and let Bluebird collect them into an array
// I think this could work just as easily with a coroutine
return Bluebird.delay(createRandomElementFromRandomTemplate(data.templates, data.userprefs), 0);
}, {concurrency: 5})
.then(generated => {
return Generated.createAsync(generated);
})
.then(results => {
res.json(results);
})
.catch(err => {
console.log(err);
res.status(500);
});
};