1

Suppose I have the following Parse cloud code:

// assume myObj is a parse object
myObj.set("field1", "foo");
Parse.Promise.as().then(function() 
  myObj.save(myObj, {
    success: function(savedObj) {
      // A
      return Parse.Promise.as();
    },
    error: function(myObj, error) {
      // B
      return Parse.Promise.as();
    }
  });
  // C
  // note that we can get here without any return statement being called
}).then(function() {
  // D
});

(Now, i know it would be simpler to just use promises for the whole thing:

myObj.save().then(
  ...

...but there are some functions that don't return promises, so sometimes you have no choice but to mix Backbone-style success/error blocks with promises.)

My question:

What happens when C is reached? Does execution pause on this promise until one of those return statements is reached, and then execution reaches D? Does execution advance directly to D after reaching C, without waiting for a return statement? Is this an error?

In other words, is it possible for execution to happen in the order C, D, A/B? Or will it always be C, A/B, D? (Or, I suppose, if save finishes crazy fast, something like A/B, C, D?)

Ben Wheeler
  • 6,788
  • 2
  • 45
  • 55
  • When working against a callback API you need to [Convert it to promises before you can use it](http://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises). Your returns are inside those `success` and `error` functions and not in the `then`. – Benjamin Gruenbaum Sep 12 '14 at 17:16
  • 2
    Removed my answer because it actually depends on how exactly `Promise` is implemented in Parse API. If it follows the recommendation of `PromiseA`, yes, a new promise is returned - and the order of operations becomes ambigious. – raina77ow Sep 12 '14 at 17:30
  • thanks, raina770w, i had liked your answer! don't know whether Parse.Promise follows that rec. – Ben Wheeler Sep 12 '14 at 17:32
  • Benjamin Gruenbaum, couldn't I in theory not convert to promises, but just do: promise = new Parse.Promise(); at C, return promise; at A and B, do promise.resolve() ? I'm pretty sure I could do that and it would work perfectly... it might be wise to convert entirely to promises, but I don't think you NEED to. There are drawbacks to promises, at least Parse.Promise's, such as situations where you need to differentiate between different causes of errors. – Ben Wheeler Sep 12 '14 at 17:35
  • You can differentiate between causes of errors. You _need_ to convert the function you're using. Lemme write up an answer. – Benjamin Gruenbaum Sep 12 '14 at 17:37
  • Actually, there's exactly what you should do - create a Promise in the `save` function and return it, then resolve/reject this promise in callbacks. – raina77ow Sep 12 '14 at 17:38

3 Answers3

3

Your returns are from the inner functions. If it was up to me I'd promisify the save function itself. However, if you're convinced you don't want to do this you still have to return a Parse.Promise from the then if you want it to wait for anything.

The then function returns a new promise and resolves that promise (executes further thens) when its return value is resolved. If that's just a value it will not wait for anything - if it's a promise it will wait for it in turn to resolve.

Please look at how to promisify for further reading.

In your example - this would look something like:

myObj.set("field1", "foo");
Parse.Promise.as().then(function(){
  var p = Parse.Promise();
  myObj.save(myObj, {
    success: p.resolve.bind(p), // on success, resolve the promise
    error: p.reject.bind(p) // on failure, reject it
  });
  // any code here _will_ execute before the `then` is called.
  return p; // return the promise so `then` will wait
}).then(function(savedObj) {
    // this will always run after `myObj.save` completed
    // you can add a catch handler for the error case
});

However, if we pay attention we can notice Parse's save method already returns a promise - Parse promisified it for us - so this code can be significantly reduced to:

myObj.set("field1", "foo");
Parse.Promise.as().then(function(){

  return myObj.save(myObj)
}).then(function(savedObj) {
  // this will always run after `myObj.save` completed
  // you can add a catch handler for the error case
});

Which in turn can be reduced to:

myObj.set("field1", "foo");
myObj.save().then(function(savedObj){
   // object save is done here
});   
Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • thanks! fyi, part of the reason i'm not collapsing my code into more concise promise-y code is that i want to maintain flexibility in what i do in different cases... eg, perhaps i want to increment a counter when the save fails. – Ben Wheeler Sep 12 '14 at 19:19
  • related question: in handling a promise error/reject, what happens after the end of a function(error) block is reached? i know that if it returns a promise that resolves or rejects, it behaves just like a regular then function() block. if it doesn't return anything, will it go right on executing the next then()? is there any way to abort the execution of the promise chain from that point? – Ben Wheeler Sep 12 '14 at 19:22
  • @BenjaminWheeler you can implement a counter anyway, increment it in the rejection handler. A promise rejection handler is exactly like a `catch` handler - the `.then` from that point will run (recovery). If you want to abort a chain simply don't catch the error - handle it at the end of the chain. – Benjamin Gruenbaum Sep 12 '14 at 19:40
  • I don't follow. I can't not "catch" the error, if there's any function(error) in the middle that's there to catch a DIFFERENT error... not clear how a counter would help, though clearly distinct variables like firstSaveFailed, secondSaveFailed, etc. would work (but be so clumsy it would make me want to go back to success/error blocks!) See my follow-up question: http://stackoverflow.com/questions/25854418/how-do-you-handle-parse-promise-errors-so-they-dont-keep-executing-code – Ben Wheeler Sep 15 '14 at 18:27
  • @BenjaminGruenbaum, as an expert you might have an opinion on this perhaps simpler question .. http://stackoverflow.com/questions/32768731 .. it sure defeats me .. thx – Fattie Sep 25 '15 at 20:27
0

Answer seems to be that it won't wait for A or B -- it'll move on, probably with D executing before A or B.

In any case, it's basically a bug -- though not one the runtime will complain about -- to invoke asynchronous code (like I do with save()) but not explicitly return a local promise at the end of a then function block that would make the bigger promise wait until the asynchronous code finishes. In other words, I should do something like Benjamin Gruenbaum's answer suggests.

Ben Wheeler
  • 6,788
  • 2
  • 45
  • 55
  • Yes. If you have something asynchronous that you want to wait for, you will need to return a promise for it. If you forget the return statement, `then` can't distinguish it from a `return undefined;` and will move on with `undefined` as the result. – Bergi Sep 13 '14 at 17:51
0

As a rule of thumb just keep returning promises, they will be automagically resolved. All Parse async methods return promises so just keep returning until you are done. I wouldn't recommend to use the success and error methods. Using callbacks and promises together is not a good idea since you have to write a lot of extra code to account for it. If everything in your app return promises you can remove a block or add a another one very easily. Just create functions that return a promise.

Also, how you return promises while vary depending on the scope needed in each block.

So for example:

function myPromise (a) {
  return Parse.Promise.as({name: 'David Bowie', a : a});
}

myObj.set("a", 1);

myObj.save(obj).then(function(obj) {
  var a = obj.get('a');
  return Parse.Promise.as()
    .then(function() {
      return Parse.Object.saveAll([])
        .then(function() {
          a += 1
          return Parse.Promise.as(a);
      })
    })
}).then(function(a){
  return myPromise(a);
})
.then(function(davidBowie) {
  console.log(davidBowie.name);
  console.log(davidBowie.a);
})
.fail(function() {
  // handle error
})
Maroshii
  • 3,937
  • 4
  • 23
  • 29
  • Why is your code nested 5 levels? Why do oyu have an excess `Parse.promise.as` there and why are you returning a `Parse.Promise.as(a)`? – Benjamin Gruenbaum Sep 12 '14 at 20:28
  • It's just an example of nested promises, it's not how code should be written. Also, you need the scope to pass `a` to the next promise that will pass it to bowie function – Maroshii Sep 12 '14 at 20:45
  • This is helpful but I'm not 100% clear how it relates to my question. – Ben Wheeler Sep 16 '14 at 15:38