0

What happens when I return a promise from a .then method which is wrapped in another object like:

somepromise
.then((step1Result) => {
  return {
    'val1' : synchronousCalculation(),
    'val2' : asyncCalculation() // << THIS HERE
  };
})
.then((result) => {
  // PATH2
});

i.e. is PATH2 guaranteed to have a settled promise in result['val2']?

A simpler situation is always guaranteed as mentioned in this question where the top answer mentions this article:

When you return something from a then() callback, it's a bit magic. If you return a value, the next then() is called with that value. However, if you return something promise-like, the next then() waits on it, and is only called when that promise settles (succeeds/fails)

Note that I'm avoiding to call await asyncCalculation() because I cannot turn the whole method into async, there's too much that would have to change if I did that.

Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63
  • No, not in this case as you are returning an object with a promise in it, which in itself is not a promise. If you turn that thrn function async and await the internal async, then yes, since you returned a promise which resolves to an object. – somethinghere Aug 16 '23 at 08:57
  • @somethinghere Could you show an example of how to do this. E.g. could I declare `async resultWrapper() : Promise { return { /* object spec with member two as promise*? }; }` and then call that in the first `.then` branch ? – Lorah Attkins Aug 16 '23 at 09:03
  • Basically add `await` before `asyncCalculation()` and also before `(step1Result) => ...` will turn your return value in a Promise, which, when received as a return value of a `then` callback, will return a new Promise with the new value chained. The next `then` in the chain will then only be called when the `asyncCalculation` completes, and the object in that then will be your object as expected. – somethinghere Aug 16 '23 at 10:09

3 Answers3

3

What happens when I return a promise from a .then method which is wrapped in another object

The same thing as with any other value of a property of that object, it is not treated in any special way.

i.e. is PATH2 guaranteed to have a settled promise in result['val2']?

No, there is no guarantee about that, result['val2'] holds a promise that could be in any state.

What you could do is something like this:

function synchronousCalculation() {
  return 1
}

function asyncCalculation() {
  return Promise.resolve(2)
}


Promise.resolve(0)
.then((step1Result) => {
  return Promise.all([
    synchronousCalculation(),
    asyncCalculation()
  ]);
})
.then(([val1, val2]) => {
  console.dir(val1)
  console.dir(val2)
});

like that:

t.niese
  • 39,256
  • 9
  • 74
  • 101
1

No, the Promise is not guaranteed to be resolved. Supposing that synchronousCalculation and asyncCalculation both take step1Result as the only argument, you could refactor your code like this:

const synchronousCalculationResult = somepromise.then(synchronousCalculation);
const asyncCalculationResult = somepromise.then(asyncCalculation);

const fullResult = Promise.all([
  synchronousCalculationResult,
  asyncCalculationResult,
]).then(([val1, val2]) => ({
  val1,
  val2,
}));
Guerric P
  • 30,447
  • 6
  • 48
  • 86
1

When Promise.then( callback )'s callback returns a Promise itself, the next chained then will await that promise. So in order to be sure all your values are resolved, you would need to turn your callback async itself.

somepromise
.then(async step1Result => {

  const result = {
    'val1' : synchronousCalculation(),
    'val2' : await asyncCalculation()
  };

  return result;

})
.then(result => {
  // This `then` now resolves to the object
  // PATH2
});

An async function basically returns a Promise that resolves whenever all await statements have been resolved. And returning Promises inside then will chain them together. Consider this example:

async function delay( d ){

  console.log( `Delaying by ${d}ms` );
    
  return new Promise(r => setTimeout(r, d));
  
}

delay( 1000 )
  .then(() => delay( 1000 ))
  .then(() => delay( 1000 ))
  .then(() => {
  
    console.log( `Delayed by 3000ms, because all promises chain` );
  
  });

You can see that after resolving the first one, the then from that promise gets called. But then does not return the same promise:

async function delay( d ){

  console.log( `Delaying by ${d}ms` );
    
  return new Promise(r => setTimeout(r, d));
  
}

const step1 = delay( 1000 );
const step2 = step1.then(() => delay( 1000 ));
const step3 = step2.then(() => delay( 1000 ));

step3.then(() => {
   
   console.log( step1 === step2 ); // False
   console.log( step2 === step3 ); // False
   console.log( `Every time a new promise got made!` );
  
 });

Every time a new Promise is returned than get's awaited. So internally, you can nest as many promises as you want. If a Promise is returned, the next step in the chain will await it being resolved, if a value is returned, the next step just gets executed with that value.

somethinghere
  • 16,311
  • 2
  • 28
  • 42