Kyle R
Kyle R

Reputation: 109

Avoiding nested promises inside a then function JS

I have an asynchronous function 'doApiRequest' that is called inside of a 'then' function...

doSomething()
  .then(() => {
    return doApiRequest();
  })
  .then((apiResult) => {
    doSomethingElse(apiResult);
  });

The issue being that doApiRequest returns a Promise with the eventual result of that API request. However, do to the nature of the API I'm working with there is a request rate limit involved. I plan on handling that by having each API request add itself to a queue, and then when the queue releases the request after waiting for the rate limit, the API request will finish resolving. While I could do something like...

doSomething()
  .then(() => {
    return waitForRateRefresh();
  })
  .then(() => {
    return doApiRequest();
  })
  .then((apiResult) => {
    doSomethingElse(apiResult);
  });

I may end up having many 'doApiRequest' calls, so having to chain a 'waitForRateRefresh' on each seems like a bad method and I'd also have to make it work so it can pass along the data from previous then statements. What I'd like to do is handle this inside of 'doApiRequest' itself.

'doApiRequest' would look something like this

doApiRequest(){
  return new Promise((resolve, reject) => {
    waitForRateRefresh().then(() => {
      //http call
      resolve(someValue);
    };
  });
}

However I'm trying to find a way to do this that doesn't involve nesting Promises. What other ways would there be to go about this. Another way I've though of doing it is by using Async/Await instead, is there any other way of doing it with just promises? What happens (or is it even possible) to return a Promise with an attached then function from 'doApiRequest' like...

return waitForRateRefresh().then(() => new Promise(..../http call));

In the original then function where 'doApiRequest' is called - will it receive the value returned by 'waitForRateRefresh', or the result of traversing down the then chain attached to it.

Thanks for any insights

Upvotes: 0

Views: 7333

Answers (2)

Jaromanda X
Jaromanda X

Reputation: 1

While async/await is great, if you're using an older version of nodejs that doesn't support it, then you'll either need to transpile async/await code, or use plain ol' Promises

Don't know if you've ever seen transpiled async/await - it's quite "verbose"

What you can do, assuming you actually want to pass the result of doSomething to doApiRequest is this

doSomething()
.then(result => waitForRateRefresh().then(() => result))
.then((doSomethingResult) => doApiRequest(doSomethingResult))
.then((apiResult) => doSomethingElse(apiResult));

of course, the above can be simplified to

doSomething()
.then(result => waitForRateRefresh().then(() => result))
.then(doApiRequest)
.then(doSomethingElse);

To clarify a point about promise constructor anti-pattern

doApiRequest(){
    return new Promise((resolve, reject) => {
        waitForRateRefresh().then(() => {
            //http call
            resolve(someValue);
        };
   });
}

this is simplified to

doApiRequest(){
    return waitForRateRefresh().then(() => {
        //http call
        return someValue;
    };
}

Of course, if // http call is asyncrhonous, then return someValue can't be used like that. But that would be the case for your version of the code as well

To accept a value from doSomething in this version of doApiRequest, change the code to

doApiRequest(someResult){
    return waitForRateRefresh().then(() => {
        //http call - use someResult here
        return someValue;
    };
}

The main code is now

doSomething()
.then(doApiRequest)
.then(doSomethingElse);

Again, though ... the //http call and return someValue will not work as desired if someValue is gathered asynchronously in whatever http call is


and one more idea so you don't need to rewrite existing functions

create a "wrapper" around doApiRequest

const qApiRequest = result => waitForRateRefresh().then(() => doApiRequest(result));

now, the code is

doSomething()
.then(qApiRequest)
.then(doSomethingElse);

Of course, with async/await it's just

const doSomethingResult = await doSomething();
await waitForRateRefresh();
const apiResult = doApiRequest(doSomethingResult);
const finalResult = doSomethingElse(apiResult);

Of course, these would need to be inside a function tagged async

Upvotes: 6

JohanP
JohanP

Reputation: 5472

I would use async/await, it keeps the code very readable and makes it easy to reason what is happening next.

await doSomething();
await waitForRateRefresh();
const apiResult  = await doApiRequest();
await doSomethingElse(apiResult);

Upvotes: 2

Related Questions