2

I'd like to understand the promises chaining in Javascript. So I wrote this little fiddle: https://jsfiddle.net/GarfieldKlon/89syq4fh/

It works like intended. c waits for b, and b waits for a.

But when I change these lines to:

a().then( result => console.log(result))
.then( () => {b().then( result => console.log(result))})
.then( () => {c().then( result => console.log(result))});

So I added the curly braces around b and c and then the output is 1, 3, 2. And I don't understand why.

When I add a return then it works again:

a().then( result => console.log(result))
.then( () => {return b().then( result => console.log(result))})
.then( () => {return c().then( result => console.log(result))});

But why? When there are no curly braces you're only allowed to write one statement, correct? And this statement is implicitly returned?

And when you use curly braces you have to explicitly return something?

But why does it mess up the order when using curly braces without return? And why does it still log something when the return is missing?

GarfieldKlon
  • 11,170
  • 7
  • 31
  • 33
  • with the curly braces, you need to return the value, that's hot fat arrow works. – AZ_ Mar 19 '19 at 08:35
  • In both the cases, the ordering is random. Returning in the `then` block is actually for the next `then` block to consume (which is not happening in your case) – varun agarwal Mar 19 '19 at 08:36
  • See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Function_body – Gabriele Petrioli Mar 19 '19 at 08:36
  • This isn't really anything to do with promises, that's just how arrow function syntax works. If you define a block body, nothing will be returned unless you do it explicitly. – jonrsharpe Mar 19 '19 at 08:40
  • @varun agarwal in which cases the ordering is random? When chaining promises, the order shouldn't be random, correct? – GarfieldKlon Mar 19 '19 at 08:44
  • Chaining a pomise is sequential but each of the chained `then` functions have a async functions in them which makes their `then` function execute in random. – varun agarwal Mar 20 '19 at 01:05

4 Answers4

2

When there are no curly braces you're only allowed to write one expression which is implicitly returned? And when you use curly braces you have to explicitly return something?

Yes.

But why does it mess up the order when using curly braces without return? And why does it still log something when the return is missing?

Because the promise then function relies on return values for chaining.

Remember that then returns a new promise for the result of the callback. When that result is another promise, it waits for the result of that inner promise before fulfilling the returned promise - the one on which you are chaining the second then() call.

If your callback starts b().then(…) but returns undefined, the next step in the chain (the c() call) doesn't wait for b to finish.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

When using the arrow syntax, you can interpret it in many things:

() => 'hello'

is equivalent to

() => { return 'hello'; }

However, when doing

() => {'hello'}

as similar to what you wrote:

.then( () => {b().then( result => console.log(result))})

then you can interpret that as

() => {'hello'; return;}

So, in the code, yours promise handling b().then( result => console.log(result)) is called before a void return; is done. You are not returning the resulting promise object at all. It gets called when b is finished, no matter of what you were doing.

KarelG
  • 5,176
  • 4
  • 33
  • 49
  • Thanks for the helpful examples. So 'c' gets called before 'b' because both aren't promises and 'c' has the shorter wait time. Correct? If yes, then I think I got it. – GarfieldKlon Mar 19 '19 at 09:28
  • @GarfieldKlon both `b` and `c` are functions that returns with a promise object. Yet, you are not returning these promise objects in the `a()....then` chain in yours example (that one without `return`). The `a.then` has received a void return, which is not a promise object. So it proceeds further instead of waiting when the promise object from `b()` is fulfilled. In the example with `return` statement, the `a.then` did have received a promise object (from `b()`) and waits for that before running the next `.then` statement. – KarelG Mar 19 '19 at 09:53
0

This has to do with arrow functions. When you omit the curly braces, the return is implicit. So

const f = () => 42;

is equivalent to

const f = () => { return 42; };

Also, b().then(something).then(somenthingElse) is still only one expression, so it can be implicitly returned.

Dan Homola
  • 3,819
  • 1
  • 28
  • 43
0

A Promise is an object that represents the result of an asynchronous operation. It is important to remember that Promises are not just abstract ways registering callbacks to run when some async code finishes — they represent the results of that async code.

When we write a chain of .then() invocations, we are not registering multiple callbacks on a single Promise object. Instead, each invocation of the then() method returns a new Promise object.

That new Promise object is not fulfilled until the function passed to then() is complete. The value that fulfills promise 1 becomes the input to the callback2() function. The callback performs some computation and returns a value v. When the callback returns the value, the promise is resolved with the value v.

When a Promise is resolved with a value that is not itself a Promise, it is immediately fulfilled with that value.

It is important to understand in this case, so I rephrase: if a callback returns a non-Promise, that return value becomes the value of the promise, and the promise is fulfilled.

If the return value v is itself a Promise, then the promise is resolved but not yet fulfilled.

Your case:

allow me to post the functions a,b and c here:

let a = function() {
    return new Promise( (resolve, reject) => {
    setTimeout(() => resolve(1), 300);
  });
}
let b = function() {
    return new Promise( (resolve, reject) => {
    setTimeout(() => resolve(2), 200);
  });
}
let c = function() {
    return new Promise( (resolve, reject) => {
    setTimeout(() => resolve(3), 100);
  });
}

and the implementation

a().then( result => console.log(result))
.then( () => {b().then( result => console.log(result))})
.then( () => {c().then( result => console.log(result))});

Here is what is happening

By adding the curly braces to the arrow function body, we no longer get the automatic return.

This function now returns undefined instead of returning a Promise, which means that the current Promise is fulfilled and next stage in this Promise chain will be invoked with undefined as its input.

So, function a gets invoked, returns a promise, which is, once fulfilled invokes the callback1. callback 1 returnes undefined so the promise1 is fulfilled and the next .then() invokes the callback2. The callback2 invokes the b which itself returnes a Promise, but this Promise is not returned by the callback2. The callback2 returns undefined. The Promise2 is fulfilled with undefined and life goes on - Callback3 gets invoked.

At this point b and c are running simultaneously, and because c is 100ms faster than b you get the 1,3,2 order.

Terminology

We say that the promise has been fulfilled if and when the first callback (argument of then method) is called.

And we say that the Promise has been rejected if and when the second callback (argument of then method) is called.

If a Promise is neither fulfilled nor rejected, then it is pending. And once a promise is fulfilled or rejected, we say that it is settled.

If the async code runs normally (and the Promise is fulfilled), then that result is essentially the return value of the code.

If the Promise is fulfilled, then the value is a return value that gets passed to any callback functions registered as the first argument of then().

Promises can also be resolved. It is easy to confuse this resolved state with the fulfilled state or with settled state, but it is not precisely the same as either.

Resolved means that another Promise has been assigned to the value of the current Promise. The current Promise has become associated with, or “locked onto,” another Promise. We don’t know yet whether the Promise will be fulfilled or rejected, because it is not yet settled. Its fate now depends entirely on what happens to Promise

mgoetz
  • 89
  • 1
  • 2