3

I've got a cloud code function which marks Notification objects as "read" and then queries the same class immediately for remaining "unread" objects.

The problem is the most times, the same Notification objects I mark as read are returned in my preceding unread query, even if the values have been updated in data browser.

When I changed from Parse.Promise.when() to Parse.Object.saveAll(), that solved the issue, but it's still unclear why. The when promise is supposed to be resolved when all the save() operations complete and hence will not run the last "unread" query until they've been marked as read.

What does Parse.Object.saveAll(objects) do that Parse.Promise.when(promiseListOfSaves) doesn't?

Code examples of each below. Not much difference between the two aside from the way objects are being saved.

Parse.Cloud.define('markThreadReadAndReturnUnreadCount', function(request, response){

    var promiseChain = [];
    var Notification = Parse.Object.extend("Notification");
    qry = new Parse.Query(Notification);
    qry.equalTo('owner', request.user);
    qry.equalTo('read', false);
    qry.equalTo('messageThreadId', request.params.threadId);
    qry.find(null, {useMasterKey: true}).then(function(_notifications){
        _.each(_notifications, function(_notification){
            notification = new Notification();
            notification.id = _notification.id;
            notification.set('read', true);
            promiseChain.push(notification.save(null, {wait: true}));
        });
        return Parse.Promise.when(promiseChain);
    }).then(function(){
        var countUnreadQuery = new Parse.Query(Notification);
        countUnreadQuery.equalTo('owner', user);
        countUnreadQuery.equalTo('read', false);
        countUnreadQuery.find({useMasterKey: true}).then(function(results){
            return response.success(results.length);
        }
    };

});

with Parse.Object.saveAll();

Parse.Cloud.define('markThreadReadAndReturnUnreadCount', function(request, response){

    var saveObjects = [];
    var Notification = Parse.Object.extend("Notification");
    qry = new Parse.Query(Notification);
    qry.equalTo('owner', request.user);
    qry.equalTo('read', false);
    qry.equalTo('messageThreadId', request.params.threadId);
    qry.find(null, {useMasterKey: true}).then(function(_notifications){
        _.each(_notifications, function(_notification){
            notification = new Notification();
            notification.id = _notification.id;
            notification.set('read', true);
            saveObjects.push(notification);
        });
        return Parse.Object.saveAll(saveObjects);
    }).then(function(){
        var countUnreadQuery = new Parse.Query(Notification);
        countUnreadQuery.equalTo('owner', user);
        countUnreadQuery.equalTo('read', false);
        countUnreadQuery.find({useMasterKey: true}).then(function(results){
            return response.success(results.length);
        }
    };

});
Tariq Zabian
  • 33
  • 1
  • 5
  • I made some copy edits mostly just to break up the one long paragraph into multiple paragraphs for better readability, in the spirit of trying help make sure you get a good answer here. (Welcome to StackOverflow, by the way.) – sideshowbarker Oct 07 '15 at 07:01
  • @Tariq, I guess you need to update the second block of code to `Object.saveAll` – mido Oct 07 '15 at 07:18
  • Thanks @mido22. Updated – Tariq Zabian Oct 07 '15 at 08:27

1 Answers1

2

I am not sure why saveAll works where when doesn't work, but I would prefer saveAll over when in this scenario for two reasons:

  1. The code style, what looks better of the two options below:

    function save(objects){
        return Parse.Promise.when(objects.map(function(object){
            return object.save(null, {wait: true});
        }));
    }
    
    // or
    
    function save(objects){
        return Parse.Object.saveAll(objects);
    }
    
  2. Performance, when you use an array of save, you send n http requests for the n objects you want to save, this is a huge waste of resources, but when you use saveAll you send only one request for all of them.

also I think your second code can be reduced to:

...
qry.find(null, {useMasterKey: true}).then(function(_notifications){
    _.each(_notifications, function(_notification){
        notification.set('read', true);
    });
    return Parse.Object.saveAll(_notifications);
}).then(function(){
...
mido
  • 24,198
  • 15
  • 92
  • 117
  • 1
    I think the main point is number 2. Read [Parse.Object.saveAll vs Parse.Promise.when](https://www.parse.com/questions/parseobjectsaveall-vs-parsepromisewhen) – Daniel B Oct 07 '15 at 08:53
  • The network call optimization makes total sense. I'm still unclear why a `Parse.Promise.when()` resolves before the save operations in the promise chain actually complete? At least, that's what seems to be happening. – Tariq Zabian Oct 07 '15 at 09:19
  • @TariqZabian try chaining each `save` with `console.log`, see if they all get executed before `then` of `Promise.when`... – mido Oct 07 '15 at 09:35