Arguments in Parse.com query.find success callback

2019-06-14 11:14发布

Thanks for the help in advance. I'm working on an practice assigment using Phonegap and Javascript. Long story short: I need to use Parse.com to store information about some Lego minifigures. The problem I'm having right now is due mostly to my inexperience in Javascript.

I'm working on letting the user add tags to the figures. The user enters them, separated by comma, and I then split the string. That's working OK.

Now, I need to add the tags that don't exist yet to my database. For this, I search for any tags with that description (using query.find) and then, if it exists, I don't create it, I just modify the relationship. If it doesn't exist, I create it and then modify the relationship.

My problem is: I can't seem to be able to access the tag description (the string) from within the success callback of query.find. I'm pretty sure it's because of the scope. Is there any proper way to access variables from withing a success callback, besides the results array?

My current code is as follows:

                var Figure = Parse.Object.extend("Figure");
                var Tag = Parse.Object.extend("Tag");
                var nombre = $('#nombre').val();
                var serie = $('#serie').val();
                var figure = new Figure({"Name":nombre,"Series":serie});
                var tags = $('#tags').val();
                res = tags.split(","); //split the 
                figure.save().then(function() {
                    for (var i = 0; i < res.length; i++) { //for each tag

                        var query = new Parse.Query(Tag); //create the query.
                        query.equalTo("Description", res[i]);
                        query.find( {//execute query                                
                            success: function(results, res[i]) {
                             if (results.length > 0){ //if there are results.
                                var tag = results[0]; //get the tag
                                var relation_tag = tag.relation("figures"); //get the relation
                                relation_tag.add(figure); //add figure to relation
                                tag.save();
                              }
                             else { //if there are no results, the tag does not exist.
                                new_tag = new Tag({"Description":res[i]}); 
                               //ABOVE THIS LINE: res[i] is always undefined.
                                var relation_tag = new_tag.relation("figures"); //get the relation
                                relation_tag.add(figure); //add the figure
                                new_tag.save();
                              }
                            },
                            //error with query
                            error: function() {
                             alert("ERROR");
                            }


                        });     

                    }                                           


                }, function(error) {

                    alert("No se pudo guardar la figura");

                });

In the success callback, res[i] always is undefined, I assume that it's because of the scope.

2条回答
Deceive 欺骗
2楼-- · 2019-06-14 11:42

This is a very common problem in async Javascript programming. You are doing something like this:

for (var i = 0; i < array.length; i++) {
    anAsyncFunction(function(result) { // inner function
        doSomethingWith(array[i]);
    }
}

The problem is that in Javascript functions store outer variables by reference and not by value, which means that a function looks up the value of a variable from an outer scope, when it is executed and not when it is defined. Since the code is async the the inner function is called after the for loop completed and at this point we have i === array.length, so array[i] === array[array.length] === undefined.

To avoid this you can use an immediately invoked function expression (IIFE, pronounced "iffy"):

for (var i = 0; i < array.length; i++) {
    anAsyncFunction((function(j) { // IIFE
        return function innerFunction(result) { // inner function
           doSomethingWith(array[j]); // j instead of i
       }
    })(i); // passing "value of i"
}

Because the IIFE is invoked immediately, the current value is of i is passed and stored into j and when the inner function executes it uses the correct value.

So in your case this should work:

success: (function(j) { // IIFE
    return function(results) {
        if (results.length > 0) {
            var tag = results[0];
            var relation_tag = tag.relation("figures");
            relation_tag.add(figure);
            tag.save();
        }
        else { //if there are no results, the tag does not exist.
            new_tag = new Tag({"Description":res[j]}); // j instead of i
             var relation_tag = new_tag.relation("figures");
            relation_tag.add(figure);
            new_tag.save();
        }
    }
})(i) // pass "value of i"

If you prefer, you can also pass the description itself instead of just the index to the IIFE (I think I would do it that way):

success: (function(description) { // IIFE
    return function(results) {
        if (results.length > 0) {
            var tag = results[0];
            var relation_tag = tag.relation("figures");
            relation_tag.add(figure);
            tag.save();
        }
        else { //if there are no results, the tag does not exist.
            new_tag = new Tag({"Description":description}); // description
             var relation_tag = new_tag.relation("figures");
            relation_tag.add(figure);
            new_tag.save();
        }
    }
})(res[i]) // pass description
查看更多
贪生不怕死
3楼-- · 2019-06-14 11:45
var Tag = Parse.Object.extend("Tag");
var query = new Parse.Query(Tag); 
查看更多
登录 后发表回答