Search code examples
promisees6-promise

Can a series of promises and then handlers be written in waitOneCycle.then(waitOneCycle().then(...)) but without deep nesting?


If we already have

const waitOneCycle = () => {
  return new Promise(resolve => setTimeout(resolve));
};

Then our code (this is currently used in Jest and SinonJS although it doesn't have to be:)

waitOneCycle()
  .then(() => {
    // do something

  });

and it reads really elegantly: the waitOneCycle and then do something.

However, if we do a series of them, we have to:

waitOneCycle()
  .then(() => {
    // do something

    return waitOneCycle();
  })
  .then(() => {
    // do something

    return waitOneCycle();
  })
  .then(() => {
    // do something

    return waitOneCycle();
  });

and it reads in a strange way, because why would we return something to act as "waitOneCycle"? This is the way it works, but the code just read in a strange way.

We could do something like:

waitOneCycle()
  .then(() => {

    // not return anything here
  })
  .then(() => {
    waitOneCycle()
      .then(() => {

      });
  })

but this would go back to the nesting hell issue in the past. Is there a way to refactor it so it reads coherently but at the same time all actions are serialized?

P.S. in the case of Ruby, if the promises are chained this way, it actually can read out quite well, as we don't need to use return for the last value evaluated. So it'd read like waitOneCycle, then, waitOneCycle, then... and it appears quite elegantly.


Solution

  • You don't need to nest (and it would only work if you were to return the inner promise anyway), you can also chain it:

    waitOneCycle()
      .then(() => {
        // do something
      })
      .then(waitOneCycle)
      .then(() => {
        // do something
      })
      .then(waitOneCycle)
      .then(() => {
        // do something
      })
      .then(waitOneCycle);
    

    This might work even better if you make waitOneCycle pass through the value:

    const waitOneCycle = (v) => {
      return new Promise(resolve => setTimeout(resolve, v));
    };
    

    Of course, if you want promise code that reads coherently and has no nesting (not even that of then callbacks), you should just go for async/await syntax.