2

I've read that nesting Promise continuations is bad practice. For example, here.

GetCustomer().then(customer => {
    ProcessCustomer(customer).then(processingResult =>
    {
        console.log(customer);
        console.log(processingResult);
    });
});

As the 2nd function call relies on the output of the first, un-nesting this looks like this...

GetCustomer().then(customer => {
        return Promise.all([customer, ProcessCustomer(customer)]);
    }).then(results => {
        console.log(results[0]);
        console.log(results[1]);
    });

I personally find the nested style easier to read and reason about. I also like the strongly named variables, as apposed to accessing results[i], which could be error prone.

What are the disadvantages of the first approach, and/or the advantages of the second?

  • Your second code block makes no sense to me at all. Why use Promise.all() in that situation? There's only one async operation - no need for Promise.all(). – jfriend00 Jun 25 '18 at 4:51
  • With multiple nesting will be more difficult to find, on which level customer was created. Where "chaining" approach will need to look only one step back. – Fabio Jun 25 '18 at 4:59
  • 1
    @jfriend00 Indeed! This was the pattern suggested in the link I posted in my question for converting from dependent nested promises. – Tom Bowers Jun 25 '18 at 6:14
  • 4
    @jfriend00 customer is used alongside processingResult. You don't hold onto it if you just GetCustomer().then(ProcessCustomer).then(processingResult => ... ) – Caleth Jun 25 '18 at 8:23
  • the async /await is like a sugar syntax for a flattened promise notation, there is a kind of convergence in the approach. – pdem Jun 25 '18 at 11:20
2

The simplest option to have access to both customer and processingResult without nesting would probably be to use async/await (ES7 feature) like this (this would need to be inside an async function declaration):

async function someFunction() {
    const customer = await GetCustomer();
    const processingResult = await ProcessCustomer(customer);
    console.log(customer);
    console.log(processingResult);
    return someValue;
}

// usage
someFunction().then(result => {
    // done here
}).catch(err => {
    console.log(err);
});

What are the disadvantages of the first approach,

At just two levels like this, there are no particular disadvantages to nesting. You MUST return the inner promise in order to be able to get final result and full error handling. So, to use the first option properly with nesting, you would need to at least return the inner promise so you can catch all errors at the top level:

GetCustomer().then(customer => {
    return ProcessCustomer(customer).then(processingResult => {
        console.log(customer);
        console.log(processingResult);
        return someValue;
    });
}).catch(err => {
    // catch all errors here
    console.log(err);
});

If you get more levels, the nesting gets less and less readable.

and/or the advantages of the second?

It's really just personal opinion at this point. I'm personally not a fan of passing a raw value to Promise.all() just so it will pass it through to a common .then() handler. It appears to make the code more complicated looking than it actually is (but that's just my opinion). It would work just fine.

This is a place where object destructing would make things a little more readable and you need a .catch() handler:

GetCustomer().then(customer => {
    return Promise.all([customer, ProcessCustomer(customer)]);
}).then(([customer, processedCustomer]) => {
    console.log(customer);
    console.log(processedCustomer);
}).catch(err => {
    console.log(err);
});

For a number of other options, see here:

How to Chain and Share Prior Results With Promises

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.