多层闭包和同步javascript

多层闭包和同步javascript,javascript,node.js,closures,synchronous,Javascript,Node.js,Closures,Synchronous,这是我上一篇文章的延伸,它得到了一个非常解释性的答案。事实证明,我没有为我的应用程序提供足够的上下文,使这个问题对我的实际情况足够有用 以下是我的Express应用程序中的路线: var eventbriteService = require('../apiRequests/eventbriteService'); var queue = require('queue-async'); app.get('/events', function (req, res, next) {

这是我上一篇文章的延伸,它得到了一个非常解释性的答案。事实证明,我没有为我的应用程序提供足够的上下文,使这个问题对我的实际情况足够有用

以下是我的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);

    });

};
代码按以下顺序执行:

  • eventbrite服务记录eventbrite对象数组(空)
  • eventbrite对象的路由日志数组(空)
  • eventbrite服务使用jsdom在html中查找现有的IMG
  • 很明显,这是不同步的,考虑到回调、闭包和队列异步的许多层,我对纠正这一点感到非常困惑


    我正在使用队列异步库定义路由中的回调,该回调最终由eventbrite服务填充。在我最近添加jsdom html解析功能之前,这一切都很好。除了解决这个紧迫的问题外,我还寻求帮助构建回调、闭包和同步代码的心智模型

    问题在于,当
    for
    循环完成时,您正在调用回调,但是
    for
    循环在其每次循环中调用异步函数(
    jsdom.env
    )。最终发生的是
    for
    循环在调用的函数完成之前完成循环

    您需要的是在所有异步函数完成后调用回调的东西。由于您已经在其他地方使用了
    queue async
    ,所以让我们使用它(请参阅修改后的代码中的注释):

    var queue=require('queue-async');
    exports.getEventbriteEvents=函数(cb){
    请求({
    uri:“https://www.eventbrite.com/json/event_search?app_key=&city=Austin&date=2014-10-01&2014-10-15",
    方法:“GET”,超时:10000,followRedirect:true,maxRedirects:10,
    },功能(错误、响应、正文){
    如果(错误)返回cb(错误);
    试一试{
    var jsdomQueue=queue();
    var eventsJSON=JSON.parse(body);
    var eventsWithAllFields=eventsJSON.events;
    var totalEventsNumber=parseInt(eventsWithAllFields[0]。summary.total\u items);
    对于(变量i=1,l=eventsWithAllFields.length;i
    这不是一个真正的答案,但是签出对于处理复杂的回调层次结构非常有用。我根本没有得到任何输出。似乎执行永远不会返回routes文件中的队列异步,因为我甚至从未记录硬编码的“此处是routes的事件”。很抱歉,我在移动该
    时遗漏了一个需要绑定的变量(
    i
    )。bind
    调用(我已编辑了答案)。仍然没有任何内容。是什么导致它无法将控制权返回到路由?嗯,如果您的回调
    cb
    从未被调用,那么就会发生这种情况。听起来像是在
    jsdomQueue中的回调。出于某种原因,waitAll
    没有被调用。哦,你注意到我在
    request
    s
    uri
    参数中更改了你的
    app\u键吗?(我想你可能不想让它到处都是。对不起,我应该提到这一点)
    
    {}
    []
    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
    
    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);
            }
    
        });
    
    };