1

I think I'm preventing nested queries as much as possible, but I'm honestly not sure. I understand the calls here can all be executed in a single select query, but I did this to simplify the example.

// This example is in TypeScript

// find user
User.find({where:{username:'user'}})

    // if found user
    .then(function(user) {

        return User.find({where:{username:'other_user'}})

            // if found other_user
            .then(function(other_user) {
                // do stuff
                return whatever_i_need
            }

            // if something went wrong, go straight to parent catch
            .catch(function(err) {
                // do stuff
                throw new Error()
            }
    }

    // if previous .then() returned success
    .then(function(data) {

        return User.find({where:{username:'yet_another_user'}})

            // if found yet_another_user
            .then(function(yet_another_user) {
                // do stuff
                return whatever_i_need_again
            }

            // if something went wrong, go straight to parent catch
            .catch(function(err) {
                // do stuff
                throw new Error()
            }
    }

    // if anything threw an error at any point in time
    .catch(function(err) {
        // handle the error
    }

However, this results in nested promises, which is exactly what promises are meant to prevent. Is this the "max depth" recommended for promises, or am I missing something? Is there a better way to chain queries?

Matt
  • 1,392
  • 12
  • 24
  • `this results in nested promises, which is exactly what promises are meant to prevent.` - there are circumstances where nested promises are necessary . Not saying this is such a case, but don't blindly believe everything you read about Promises :p – Jaromanda X Aug 29 '16 at 02:26

2 Answers2

2

Return the nested promise instead of handling it in the inner blocks to flatten the structure.

User.find({where:{username:'user'}})
.then(function(user) {
  if (user) { // if found user
    // do stuff
    return User.find({where:{username:'other_user'}});
  }
  throw new Error('user not-found');
})
.then(function(other_user) {
  if (other_user) { // if found other_user
    // do stuff
    return whatever_i_need;
  }
  throw new Error('other_user not-found');
})
.then(function(data) {
  return User.find({where:{username:'yet_another_user'}})
})
.then(function(yet_another_user) {
  if (yet_another_user) { // if found yet_another_user
    // do stuff
    return whatever_i_need_again;
  }
  throw new Error('yet_another_user not-found');
}
.then(function(data){
  // do stuff
})
.catch(function(err) { // if anything threw an error at any point in time
  // handle the error
}

Note that a resolved promise means a query is successfully done. That's it all about. A successful query does't guarantee results to be returned. Empty result is a valid outcome of resolved promises.

Note also that the return value from a resolve or reject callback will be wrapped with a resolved promise, and then passed to the next then block, making a meaningful promise chain. Thanks for @Matt's follow-up feedback below regarding this point.

Season
  • 4,056
  • 2
  • 16
  • 23
  • So this flow uses a `.then()` for each query and another `.then()` to analyze that result? – Matt Aug 29 '16 at 02:48
  • @Matt, yes, and that is how the flow is flattened. – Season Aug 29 '16 at 02:52
  • @Matt, I was assuming that you want to throw errors to exit from the chain early if some interested user is not found. If this is not the case, handle it in any proper way otherwise. – Season Aug 29 '16 at 02:55
  • Where are all those `if`s coming from? – Bergi Aug 29 '16 at 02:56
  • @Bergi, they are meant to confirm that an interested user has been found when the promise is resolved. Though that depends on the behavior of `User.find`, I was trying to give a notice on this. – Season Aug 29 '16 at 03:08
  • @Bergi, if no row was found in the table, the promise still resolves. The value null is returned. So guy check that you're data is good. – Matt Aug 29 '16 at 13:04
  • @Season what's the difference between your `return Promise.resolve(whatever_i_need)` and `return whatever_i_need`? Does the Promise.resolve handle cleanup? – Matt Aug 29 '16 at 16:22
  • Hi @Matt, they are identical, since the returned value from a resolved/rejected callback will be wrapped in a resolved promise behind the scene. For simplicity I do prefer not to wrap the returned value myself. I will edit my post to address this. Nice feedback. – Season Aug 29 '16 at 16:39
1

Two points:

  • Drop .catch(function(err) { throw new Error() }. It does nothing but remove the error message.
  • You can unnest the inner then calls

So it just should be

User.find({where:{username:'user'}})
.then(function(user) {
    return User.find({where:{username:'other_user'}})
})
.then(function(other_user) {
    // do stuff
     return whatever_i_need
})
// if previous .then() returned success
.then(function(data) {
    return User.find({where:{username:'yet_another_user'}})
})
// if found yet_another_user
.then(function(yet_another_user) {
    // do stuff
    return whatever_i_need_again
})
// if anything threw an error at any point in time
.catch(function(err) {
    // handle the error
})
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This is similar to the above answer, right? Use 2 `.then()` calls for each query. The first `.then()` to make a subsequent query and the second `.then()` to handle the result? Also, nice catch on the overwriting errors. I mixed some code up, those were supposed to be `if(row is valid) { return data } else { throw new Error() }`. – Matt Aug 29 '16 at 03:00
  • @Matt Ah, I see, yes they are similar. You don't need 2 `then`s if you're not doing 2 asynchronous things, you can usually merge a promise-returning call into the previous synchronous call. Btw, if that logic is the same everywhere, you might want to abstract it out into an extra function. – Bergi Aug 29 '16 at 07:42