0
votes

As the title states, I'm having trouble with Promises in Parse.

I'm struggling to firstly understand exactly how Promises themselves work, especially in Parse.

I have been stuck on this for about three weeks and the closest I've come to a solution is having an empty array returned.

What I'm trying to do is scrape a site and then create objects from the table (this is working)

Where there trouble comes in, is I am then running a for loop on the results and querying each Dam name to get the resulting objectid from the database.

Here is my code:

var c = new Crawler({
    maxConnections: 10,
    // This will be called for each crawled page
    callback: function(err, res, done) {
        if (err) {
            console.log(err);
        } else {
            var $ = res.$;
            // $ is Cheerio by default
            //a lean implementation of core jQuery designed specifically for the server
            console.log($("title").text());
        }
        done();
    }
});

The Function which Creates objects from the Dom and adds them to an array:

function getDamObjects(Dom) {
    var dom = Dom;
    var LevelObjects = [];

    for (i = 1; i < dom.length - 1; i++) {
        var TableRow = dom.eq(i);

        var NameString = TableRow.children().eq(0).text();
        var RiverString = TableRow.children().eq(1).text();
        var FSCString = TableRow.children().eq(4).text();
        var ThisWeekString = TableRow.children().eq(5).text();
        var LastWeekString = TableRow.children().eq(6).text();
        var LastYearString = TableRow.children().eq(7).text();

        NameString = NameString.replace('#', '');
        NameString = NameString.replace('$', '');
        NameString = NameString.replace('&', '');
        NameString = NameString.replace('@', '');

        ThisWeekString = ThisWeekString.replace('#', '');
        ThisWeekString = ThisWeekString.replace('$', '');
        ThisWeekString = ThisWeekString.replace('&', '');
        ThisWeekString = ThisWeekString.replace('@', '');

        LastWeekString = LastWeekString.replace('#', '');
        LastWeekString = LastWeekString.replace('$', '');
        LastWeekString = LastWeekString.replace('&', '');
        LastWeekString = LastWeekString.replace('@', '');

        LastYearString = LastYearString.replace('#', '');
        LastYearString = LastYearString.replace('$', '');
        LastYearString = LastYearString.replace('&', '');
        LastYearString = LastYearString.replace('@', '');

        var level = {};
        /*
        getDamObject(NameString).then(function(DamObject){
        let DamID = DamObject.id;
        */
        level['Dam'] = NameString; //DamID;
        level['ThisWeek'] = ThisWeekString;
        level['LastWeek'] = LastWeekString;
        level['LastYear'] = LastYearString;

        LevelObjects.push(level);
    };
    return LevelObjects;
};

The Get Dam Object Code:

function getDamObject(Dam) {
    var promise = new Parse.Promise();

    var query = new Parse.Query("DayZeroDams");
    query.equalTo("Name", Dam);
    query.first().then(function(DamObject) {
        promise.resolve(DamObject);
    }, function(error) {
        promise.reject(error);
    });

    return promise;
}

The Cloud Code Called:

Parse.Cloud.define('jsdom', function(request, response) {
    c.queue([{
        uri: 'xxxxxx',
        // The global callback won't be called
        callback: function(err, res, done) {
            if (err) {
                response.error(err);
            } else {
                var $ = res.$;
                var ResultsArray = [];
                var dom = res.$('#mainContent_tw').children('tr');
                return Parse.Promise.as().then(function() {
                    var promise = Parse.Promise.as();
                    var LevelObjects = getDamObjects(dom);
                    _.each(LevelObjects, function(DamLevel) {
                        promise = promise.then(function() {
                            var Name = DamLevel["Dam"];
                            var query = new Parse.Query("DayZeroDams");
                            query.equalTo("Name", Name);
                            return query.first().then(function(result) {
                                let damID = result.id;
                                ResultsArray.push(damID);
                                return Parse.Promise.as();
                            }, function(error) {
                                response.error(error);
                            });
                        });
                    });
                    return promise;
                }).then(function() {
                    response.success(ResultsArray);
                }, function(error) {
                    response.error(error);
                });
                //response.success(LevelObjects);
            }
            done();
        }
    }]);
});

Please take note that I am fairly novice when it comes to Javascript, I have only recently started learning it in order to work with my server code.

1
Consider using indentation while typing code, it'll make things easier for you and for anyone else who has to read it. - CertainPerformance
@CertainPerformance sorry about that ^^ I'm new here and I wasn't sure how to format my code properly since all my indentation changed when I pasted it here so I just followed the four spaces rule. - BLE
In short, make getDamObjects an async function, make each asynchronous call into a promise, and await each of them. Or, if waiting in serial is not what you want, use await Promise.all instead. - CertainPerformance
@CertainPerformance I want it in serial (I think I do at least). Would you be able to post a solution or at least an example which could point me in the right direction :) - BLE
@CertainPerformance PS is there any reason why you commented out the cloud code function? If it's badly written I'm sorry - BLE

1 Answers

1
votes

Convert getDamObjects into an async function and then await the result of each row, pushing it to the array:

function replaceSymbols(input) {
  return input.replace(/[#\$&@]/g, '');
}
async function getDamObjects(Dom) {
  const dom = Dom;
  const levelObjects = [];
  for (let i = 1; i < dom.length - 1; i++) {
    const children = dom.eq(i).children();

    const NameString = replaceSymbols(children.eq(0).text());
    const RiverString = children.eq(1).text();
    const FSCString = children.eq(4).text();
    const ThisWeek = replaceSymbols(children.eq(5).text());
    const LastWeek = replaceSymbols(children.eq(6).text());
    const LastYear = replaceSymbols(children.eq(7).text());

    const Dam = await getDamObject(NameString);
    levelObjects.push({
      Dam,
      ThisWeek,
      LastWeek,
      LastYear,
    });
  }
  return levelObjects;
}

Remember that now that getDamObjects is an async function, it will return a Promise that resolves to the array once iterations are complete. Consume it using await getDamObjects in another async function (or use .then)