多层闭包和同步javascript [英] multiple layers of closures and synchronous javascript

查看:155
本文介绍了多层闭包和同步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

代码按以下顺序执行:


  1. eventbrite服务记录

  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆