[英]multiple layers of closures and synchronous javascript
这是我先前问题的扩展,该问题得到了非常解释性的答案。 事实证明,我没有为我的应用程序提供足够的上下文,无法使问题对我的实际情况足够有用。
这是我的Express应用中的一条路线 :
var eventbriteService = require('../apiRequests/eventbriteService');
var queue = require('queue-async');
app.get('/events', function (req, res, next) {
queue()
.defer(eventbriteService.getEventbriteEvents)
.await(function(err, data) {
if (err) {return next(err);}
console.log("here are the events from routes" ,data);
});
});
这条路线呼叫以下服务 :
exports.getEventbriteEvents = function (cb) {
var eventbriteEvents = [];
request({
uri: "https://www.eventbrite.com/json/event_search?app_key=R3DQQXPXBTSMUWVNOV&city=Austin&date=2014-10-01&2014-10-15",
method: "GET", timeout: 10000, followRedirect: true, maxRedirects: 10,
}, function(err, response, body){
if (err) return cb(err);
try {
var eventsJSON = JSON.parse(body);
var eventsWithAllFields = eventsJSON.events;
var totalEventsNumber = parseInt(eventsWithAllFields[0].summary.total_items);
for (var i = 1, l = eventsWithAllFields.length; i < l; i++) {
var eventObject = {
name: eventsWithAllFields[i].event.title,
images: []
};
var jsdom = require('jsdom');
var arrayOfImgs = [];
jsdom.env({
html: eventsWithAllFields[i].event.description,
scripts: ["http://code.jquery.com/jquery.js"],
done: function(evtobj, errors, window) {
window.$('img').each(function(){
var imgSrc = window.$(this).attr('src');
console.log(imgSrc);
evtobj.images.push(imgSrc);
});
eventbriteEvents.push(evtobj);
}.bind(null, eventObject)
});
}
} catch(err) {
console.log(err);
return cb(err);
}
console.log(eventbriteEvents);
cb(null, eventbriteEvents);
});
};
这是我的控制台输出:
{}
[]
here are the events from routes []
https://evbdn.eventbrite.com/s3-s3/eventlogos/90039995/about.png
https://evbdn.eventbrite.com/s3-s3/eventlogos/90039995/bluedawntour1.jpg
该代码按以下顺序执行:
显然,这是不同步的,考虑到回调,闭包和队列异步的许多层,我对此更感到困惑。
我正在使用queue-async库在路由中定义一个回调,该回调最终由eventbrite服务填充。 直到我最近添加了jsdom html解析功能,该方法才能正常工作。 除了解决这个迫在眉睫的问题之外,我还在寻求构建我的回调,闭包和同步代码的思维模型的帮助。
问题是,当您的for
循环完成时,您正在调用回调,但是for
循环在其循环的每一遍都在调用异步函数( jsdom.env
)。 最终发生的是for
循环在其调用的函数完成之前完成循环。
您需要的是在所有这些异步功能完成后将调用回调的东西。 由于您已经在其他地方使用了queue-async
,所以我们就使用它(请参阅修改后的代码中的注释) :
var queue = require('queue-async');
exports.getEventbriteEvents = function (cb) {
request({
uri: "https://www.eventbrite.com/json/event_search?app_key=<redacted>&city=Austin&date=2014-10-01&2014-10-15",
method: "GET", timeout: 10000, followRedirect: true, maxRedirects: 10,
}, function(err, response, body){
if (err) return cb(err);
try {
var jsdomQueue = queue();
var eventsJSON = JSON.parse(body);
var eventsWithAllFields = eventsJSON.events;
var totalEventsNumber = parseInt(eventsWithAllFields[0].summary.total_items);
for (var i = 1, l = eventsWithAllFields.length; i < l; i++) {
var eventObject = {
name: eventsWithAllFields[i].event.title,
images: []
};
var jsdom = require('jsdom');
// push ("defer") the jsdom.env async function on to the jsdomQueue
// note: we have to move the .bind for that to still work
jsdomQueue.defer(function(i, evtobj, deferCallback) {
jsdom.env({
html: eventsWithAllFields[i].event.description,
scripts: ["http://code.jquery.com/jquery.js"],
done: function(errors, window) {
window.$('img').each(function(){
var imgSrc = window.$(this).attr('src');
console.log(imgSrc);
evtobj.images.push(imgSrc);
});
// call the deferCallback with the evtobj
deferCallback(null, evtobj);
}
});
}.bind(null, i, eventObject));
}
// wait for all the previously deferred functions to finish.
// the objects passed to deferCallback above will be in the
// eventbriteEvents array.
jsdomQueue.awaitAll(function(err, eventbriteEvents) {
if (err) {
// any additional error handling
cb(err);
} else {
// now you can call your callback
console.log(eventbriteEvents);
cb(null, eventbriteEvents);
}
});
} catch(err) {
console.log(err);
return cb(err);
}
});
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.