多层闭包和同步javascript [英] multiple layers of closures and synchronous javascript
问题描述
这是我之前的问题,其中收到了一个非常解释性的答案。事实证明,我没有提供足够的上下文我的应用程序,使问题对我的实际情况足够有用。
This is an extension to my previous question, which received a very explanatory answer. It turns out that I did not provide enough context to my app to make the question useful enough for my actual situation.
这是我的快速应用程式中的路线:
Here is a route within my Express app:
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
代码按以下顺序执行:
- eventbrite服务记录
- eventbrite服务使用jsdom在html中查找现有的imgs
显然这是不同步的,我很纠结整理这个,考虑到很多层的回调,闭包和队列-async。
Obviously this is out of sync, and I am quite confused on rectifying this, considering the many layers of callbacks, closures, and queue-async.
我使用队列异步库在路由中定义一个回调,最终被eventbrite服务填充。这工作正常,直到我最近添加了jsdom html解析功能。除了解决这个直接的问题,我寻找帮助构建我的回调,闭包和同步代码的心理模型。
I'm using the queue-async library to define a callback in the route which ends up being populated by the eventbrite service. This was working fine until I recently added the jsdom html-parsing functionality. In addition to solving this immediate problem, I am looking for help structuring my mental model of callbacks, closures, and synchronous code.
推荐答案
问题是,当
循环结束,但是 for
循环在它的每个循环中调用一个异步函数( jsdom.env
循环。最终会发生的是 for
循环在它调用的函数完成之前完成循环。
The problem is that you're calling your callback when your for
loop is finished, but the for
loop is calling an asynchronous function (jsdom.env
) on each pass of its loop. What ends up happening is the for
loop finishes looping before the functions it calls are complete.
你需要的是当所有异步函数都完成时调用回调的东西。由于您已在其他地方使用 queue-async
,我们只需使用(请参阅修改后的代码中的注释):
What you need is something that will call a callback when all those asynchronous functions are complete. Since you're already using queue-async
elsewhere, let's just use that (see comments in modified code):
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);
}
});
};
这篇关于多层闭包和同步javascript的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!