Ken M. Haggerty
Ken M. Haggerty

Reputation: 26198

Really confused re: how to use Parse Promises correctly in Cloud Code

Instead of nesting various functions within success completion handlers, I'd like to separate out my functions so that they can either be used independently or in succession. However, I'm super confused regarding the syntax of promises in Parse and have not been able to chain them together successfully despite looking at many other StackOverflow answers (e.g., here, here, here, and here) and Parse's official documentation, overview, and blog post about the topic.

Here's an example of my Cloud Code where I am trying to use chained promises:

Parse.Cloud.beforeSave(Parse.User, function(request, response) {
    var account = request.object;
    if (account.existed()) {
        if (account.dirtyKeys().indexOf("username") >= 0) {
            updateUsernameForAccountId(account.get("username"), account.id)
                .then(
                    function() {
                        console.log("Checkpoint SUCCESS");
                        response.success();
                    }, function(error) {
                        console.log("Checkpoint ERROR");
                        response.error(error);
                    });
        }
    }
});

function updateUsernameForAccountId(username, accountId) {
    console.log("Checkpoint A-1 ("+username+", "+accountId+")");
    updateUsernameForMessagesSentByAccountId(username, accountId)
        .then(
            function() {
                console.log("Checkpoint A-2");
                return updateUsernameForMessagesReceivedByAccountId(username, accountId);
            })
        .then(
            function() {
                console.log("Checkpoint A-3");
                return Parse.Promise.as();
            }, function(error) {
                console.log("Checkpoint A-ERROR");
                return Parse.Promise.error(error);
            });
};

function updateUsernameForMessagesSentByAccountId(username, accountId) {
    console.log("Checkpoint B-1");
    var query = new Parse.Query(parseClassMessage);
    query.equalTo(parseKeySenderId, accountId);
    query.find()
        .then(
            function(results) {
                console.log("Checkpoint B-2");
                var object;
                for (var i = 0; i < results.length; i++) {
                    console.log("Checkpoint B-2-"+i);
                    object = results[i];
                    object.set(parseKeySenderUsername, username);
                }
                return Parse.Promise.as(results);
            })
        .then(
            function(objects) {
                console.log("Checkpoint B-3");
                return Parse.Object.saveAll(objects);
            })
        .then(
            function() {
                console.log("Checkpoint B-4");
                return Parse.Promise.as();
            }, function(error) {
                console.log("Checkpoint B-ERROR");
                return Parse.Promise.error(error);
            });
};

function updateUsernameForMessagesReceivedByAccountId(username, accountId) {
    console.log("Checkpoint C-1");
    var query = new Parse.Query(parseClassMessage);
    query.equalTo(parseKeyRecipientId, accountId);
    query.find()
        .then(
            function(results) {
                console.log("Checkpoint C-2");
                var object;
                for (var i = 0; i < results.length; i++) {
                    console.log("Checkpoint C-2-"+i);
                    object = results[i];
                    object.set(parseKeyRecipientUsername, username);
                }
                return Parse.Promise.as(results);
            })
        .then(
            function(objects) {
                console.log("Checkpoint C-3");
                return Parse.Object.saveAll(objects);
            })
        .then(
            function() {
                console.log("Checkpoint C-4");
                return Parse.Promise.as();
            }, function(error) {
                console.log("Checkpoint C-ERROR");
                return Parse.Promise.error(error);
            });
};

The expected behavior is:

I am able to upload this code to Cloud Code successfully, but when I go into the data browser on Parse.com and change the username of an existing User object to test, the log produces the following output:

E2015-08-11T21:16:23.467Z]v27 before_save triggered for _User as master:
  Input: {"original":{"createdAt":"2015-08-09T16:53:47.194Z","objectId":"mkTluyGbHf","updatedAt":"2015-08-11T17:23:19.523Z","username":"[email protected]"},"update":{"username":"[email protected]"}}
  Result: TypeError: Cannot call method 'then' of undefined
    at updateUsernameForAccountId (main.js:114:4)
    at main.js:16:4
I2015-08-11T21:16:23.533Z]Checkpoint A-1 ([email protected], mkTluyGbHf)
I2015-08-11T21:16:23.534Z]Checkpoint B-1

And I also receive the following error in the Parse.com data browser:

Error: TypeError: Cannot call method 'then' of undefined at updateUsernameForAccountId (main.js:114:4) at main.js:16:4

Please let me know what additional information or clarification I can provide. I apologize in advance that I am not able to articulate my question, errors, or confusions more clearly.

Upvotes: 1

Views: 352

Answers (1)

Ken M. Haggerty
Ken M. Haggerty

Reputation: 26198

I solved it myself!!

The issue was that, as the error clearly stated, my method updateUsernameForAccountId wasn't returning a Promise object. However, the issue was within updateUsernameForMessagesSentByAccountId and updateUsernameForMessagesReceivedByAccountId, as executing query.find() is an asynchronous method and so will not return whatever is in its completion handler immediately.

In order for a function to return a Promise...you must return a Promise.

And so the answer was to make sure to return query.find().

Upvotes: 2

Related Questions