Is wrapping a promise in a promise an anti-pattern

2019-04-08 11:16发布

问题:

I was trying to write code for reconnecting to a database with a timeout using a Promise API.

What I ended up doing in the end was wrapping the promise to connect to the DB in a promise, but I'm not sure if that's the best way to do things. I thought there might be a way to use the original promise from trying to connect to the db, but I couldn't figure it out.

function connect(resolve) {
  console.log('Connecting to db...');
  MongoClient.connect(url, { promiseLibrary: Promise })
    .then((db) => resolve(db))
    .catch((err) => {
      console.log('db connection failed!:\n', err);
      if (retry++ < 3) {
        console.log('Trying again...');
        setTimeout(() => connect(resolve), 5000);
      } else {
        console.log('Retry limit reached!');
      }
    });
}

module.exports = new Promise(connect);

I think it would be possible without the setTimeout block, but I couldn't work around it.

回答1:

Here's a slightly more general solution:

function withRetry(asyncAction, retries) {
  if (retries === 0) {
    // Promise.resolve to convert sync throws into rejections.
    return Promise.resolve().then(asyncAction); 
  }
  return Promise.resolve()
    .then(asyncAction)
    .catch(() => withRetry(asyncAction, retries - 1));
}

This function will take a function that returns a promise, and a number of retries, and retry the function as many times as retries, if the Promise rejects.

If it resolves, the retry chains stops.

In your case:

let connectionPromise = withRetry(connect, 3); // connect with 3 retries if fails.