0

I am trying to use Promises with my async functions. From my understanding promises are supposed to alleviate callback hell. However it seems my code is still very deeply nested.

Am I missing something about promises (could this be rewritten to be more readable/idiomatic) or do promises nest just like callbacks sometimes?

In this example I'm building a function that calls an API to retrieve a friends list from some social media account. If it the initial call fails due to authentication errors, I know I have to authenticate first and do the call again.

const myRequest = { /* custom instance of 'request-promise' */ }

function getFriendsList() {
  return new Promise((resolve, reject) => {
    myRequest.get('/getFriendList').then(resp => {
      if (needsAuthentication(resp)) {
        myRequest.post('/auth', credentials).then(() => {
          myRequest.get('/getFriendList').then(resp => resolve(resp.body))
        }).catch(err => {
          reject(err)
        })
      } else {
        resolve(resp.body)
      }
    }).catch(err => {
      reject(err)
    })
  })
}

function authenticate() {
  return new Promise((resolve, reject) => {
    getCredentials().then(credentials => {
      myRequest.post('/auth').then(resp => {
        return resp.statusCode == '200' ? resolve(resp) : reject(resp)
      })    
    }).catch(err => reject(err))
  })
}
curious-cat
  • 421
  • 1
  • 7
  • 17
  • Yes, what you're missing is that promises are designed to be chained, not nested. The way you wrap existing promises in your own promise (with `new Promise((resolve, reject) => {})`) is an anti-pattern. – Lennholm Jan 13 '18 at 14:58
  • A promise chain with nested promise(s) can only sometimes be flattened. On other occasions nesting can be vital. – Roamer-1888 Jan 14 '18 at 09:17
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Jan 24 '18 at 07:53

2 Answers2

1

If a Promise is resolved with a Promise, itself does not resolve but rather waits for the passed Promise to resolve. So the Promise chain is flattened. Same applies to returning a Promise from a then chain. That's quite useful:

function getFriendsList() {
  return myRequest.get('/getFriendList').then(resp => {
    if (needsAuthentication(resp)) {
      return myRequest.post('/auth', credentials).then(() => 
        myRequest.get('/getFriendList').then(resp => resp.body)
      );         
    } else {
      return resp.body;
    }
  });
}

function authenticate() {
  return getCredentials()
    .then(credentials => myRequest.post('/auth'))
    .then(resp => {
      if(resp.statusCode == '200'){
        return resp;
      } else {
        throw resp;
      }
    })    
}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • You might want to [unnest](https://stackoverflow.com/a/22000931/1048572) the `.then(resp => resp.body)` part – Bergi Jan 24 '18 at 07:55
1

To prevent

callback hell

We have the Promise way, but now to prevent the

Promise hell

We have the async/await syntax

Your code should be like this

async function getFriendsList() {
    var resp = await myRequest.get('/getFriendList')
    if (needsAuthentication(resp)) {
        await myRequest.post('/auth', credentials)
        var resp2 = await myRequest.get('/getFriendList')
        return resp2.body
    } else {
        return resp.body
    }
}

async function authenticate() {
    var credentials = await getCredentials()
    var resp = await myRequest.post('/auth')
    return resp
}

Always remember that any Promise.reject() or any throw Error inside your async function will result in a rejected function (Promise).

So, in your authenticate function

async function authenticate() {
    var credentials = await getCredentials()
    var resp = await myRequest.post('/auth') //If this get rejected
    return resp
}

authenticate().then().catch(error => console.log('error here', error))

Always remember

async functions are just Promises, that resolve with the return and reject when inside its code something reject or throw an Error

Fernando Carvajal
  • 1,869
  • 20
  • 19