Question

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?

Was it helpful?

Solution

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

Licensed under: CC-BY-SA with attribution
scroll top