2

I have a Parse Server app hosted on back4app and I am running a Background Job that runs every minute and queries the Letters class, where the column deliveryDate is less or equal to the current date, here's my main.js file:

// DELIVER A LETTER
Parse.Cloud.job("deliverLetter", function (request, status) {
    var now = new Date();
    // var nowTime = date.getTime();

    var Letters = Parse.Object.extend("Letters");
    var query = new Parse.Query(Letters);
    query.lessThanOrEqualTo("deliveryDate", now);
    query.find().then (function (objects) {
        objects.forEach(function (obj) {
            obj.set("isDelivered", true);

            Parse.Cloud.useMasterKey();
            obj.save(null, { useMasterKey: true } ).then(function(obj) {
                response.success(obj);
            }, function(error) {
                response.error(error)
            });
    });
});

So, for instance, I save a row in the Letters class where deliveryDate is set to yesterday, in order for me to test this Cloud Code function. There's another column called isDelivered and it's set to False. So, my function above should set isDelivered into True and update my Letters's object.

But it doesn't work, so I don't know what I'm doing wrong.

Edit

Thanks to danh, I've fixed my code as it follows:

var Letters = Parse.Object.extend("Letters");
    var query = new Parse.Query(Letters);
    query.lessThanOrEqualTo("deliveryDate", now);
    query.equalTo("isDelivered", false);

    query.find().then (function (objects) {
        let savePromises = objects.map(function (obj) {
            obj.set("isDelivered", true);
            return obj.save(null, { useMasterKey: true } );
        });
        Promise.all(savePromises).then(function(obj) {
            response.success(obj);
        }, function(error) {
            response.error(error)
        });
    });

I would need to call another function from my main.js file which sends a push notifications and needs some parameters. I usually call it from my app, how would I call it from within that above function?

Parse.Cloud.define("pushiOS", function(request, response) {

  var user = request.user;
  var params = request.params;
  var userObjectID = params.userObjectID
  var data = params.data

  var recipientUser = new Parse.User();
  recipientUser.id = userObjectID;

  var pushQuery = new Parse.Query(Parse.Installation);
  pushQuery.equalTo("userID", userObjectID);

  Parse.Push.send({
    where: pushQuery,
    data: data
  }, { success: function() {
      console.log("#### PUSH SENT!");
  }, error: function(error) {
      console.log("#### PUSH ERROR: " + error.message);
  }, useMasterKey: true});
  response.success('success');
});

Maybe into Promise.all()?

Promise.all(savePromises).then(function(obj) {
     response.success(obj);

Parse.Cloud.define("pushiOS"...

}, function(error) { 
    response.error(error)
});
halfer
  • 19,824
  • 17
  • 99
  • 186
Frank Eno
  • 2,581
  • 2
  • 31
  • 54

1 Answers1

1

Any save() that's in progress or not yet started when response.success() is called will be terminated (or won't get a chance to start). To fix, collect promises for all of the saves, and run them together with Promise.all(), which resolves only after all of the promises passed to it have resolved.

query.find().then (function (objects) {
    // Parse.Cloud.useMasterKey(); don't need this
    let savePromises = objects.map(function (obj) {
        obj.set("isDelivered", true);
        return obj.save(null, { useMasterKey: true } );
    });
    Promise.all(savePromises).then(function(obj) {
        response.success(obj);
    }, function(error) {
        response.error(error)
    });
});

Also, note, incidentally, that query.lessThanOrEqualTo("deliveryDate", now); will get all of the objects with deliveryDates before now, including the ones you processed previously. That result will get monotonically longer over time, eventually exceeding the 1 minute between runs, or blowing some other system resource.

Maybe you really want...

query.lessThanOrEqualTo("deliveryDate", now);
query.equalTo("isDelivered", false);

EDIT

The second question can be handled by factoring a promise-returning push function like this...

function pushDataToUser(userID, data)
    var recipientUser = new Parse.User();
    recipientUser.id = userID;
    let pushQuery = new Parse.Query(Parse.Installation);
    pushQuery.equalTo("userID", userID);
    return Parse.Push.send({ where:pushQuery, data:data });
}

That can be called from cloud code like this...

Parse.Cloud.define("pushiOS", function(request, response) {
    let params = request.params;
    pushDataToUser(params.userObjectID, params.data).then(function() {
        console.log("#### PUSH SENT!");
        response.success('success');
    }, function(error) {
        console.log("#### PUSH ERROR! " + JSON.stringify(error));
        response.error(error);
    });
}

...and the new promise-returning function can be added to any other promise chain like this...

// function doSomething() returns a promise (like from a find() or a save()
return doSomething().then(function() {
    // initialize someId and someData
    return pushDataToUser(someId, someData)
});
danh
  • 62,181
  • 10
  • 95
  • 136
  • Thanks so much for your answer, what is 'Promise' though? maybe you meant 'Letters'?? – Frank Eno Oct 13 '18 at 04:21
  • :-) Promise is the object that you send `then` to. Query.find and Object.save both return promises. And Promise.all() runs a group of them. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise – danh Oct 13 '18 at 04:22
  • I see, I didn't know that, thanks, I'll give a try to your code right now ;) – Frank Eno Oct 13 '18 at 04:24
  • Sure. Here's a parse-specific doc...https://docs.parseplatform.org/js/guide/#promises It's my favorite part of JS that promises have made async programming so simple. There's a more modern syntax called async-await which you should also check out. – danh Oct 13 '18 at 04:26
  • thanks so much again, it works like a charm, I've edited my question, hopefully you may help me with a new issue as well ;) anyway, i've accepted your answer. – Frank Eno Oct 13 '18 at 04:36
  • sure. see edit. that other question would also be appropriate split off into it's own question – danh Oct 13 '18 at 04:49