Repeat a Promise until it's not rejected or reach a timeout

10,879

As Mike McCaughan mentioned, you can use setTimeout to create a delay between attempts. Resolve or reject your promise once it succeeds or you run out of attempts.

function createPromise(tries, willFail) {
  return new Promise(function cb(resolve, reject) {
    console.log(tries + ' remaining');
    if (--tries > 0) {
      setTimeout(function() {
        cb(resolve, reject);
      }, 500);
    } else {
      if (willFail) {
        reject('Failure');
      } else {
        resolve('Success');
      }
    }
  });
}

// This one will fail after 3 attempts
createPromise(3, true)
  .then(msg => console.log('should not run'))
  .catch(msg => {
    console.log(msg);
    
    // This one will succeed after 5 attempts
    return createPromise(5, false);
  })
  .then(msg => console.log(msg))
  .catch(msg => console.log('should not run'));

Share:
10,879
tdc
Author by

tdc

Updated on June 19, 2022

Comments

  • tdc
    tdc almost 2 years

    I'm still a Promise noob and am trying to figure out how to have my Promise repeat itself.

    have an ES6 promise that rejects if some global flag is not set. I need it to retry every 500ms until either:

    • the promise returns a resolve,
    • or a max number of attempts are reached (lets say 10).

    Since Promises are async, I don't really want to use a setInterval() check, because I don't think that'd work correctly with asynchronous code. I need the checking to terminate as soon as the promise is resolved successfully ( or timeout reached ).

    I'm using ES6 + React + ES6 Promises (so no Q or Bluebird-specific answers, please!)

    http://jsfiddle.net/2k2kz9r9/8/

    // CLASS
    class Test extends React.Component {
        constructor() {
          this.state = {
            status: 'setting up..',
          }
        }
        componentDidMount() {
          // TODO: how do I get this to loop with a timeout?
          this.createSlot()
            .then((slot) => {
              this.setState({
                status: slot
              });
            })
            .catch((e) => {
              this.setState({
                status: e.message
              });
            })
        }
        createSlot() {
          return new Promise((resolve, reject) => {
            if (!this.checkIsReady()) {
              reject(new Error('Global isnt ready yet'));
            }
            // more stuff here but going to resolve a string for simplicity sake
            resolve('successful!');
          });
        }
        checkIsReady() {
          return window.globalThing && window.globalThing === true;
        }
        render() {
            return ( <div>{this.state.status}</div> );
        }
    }
    
    
    
    
    
        // RENDER OUT
        React.render(< Test/> , document.getElementById('container'));
    

    EDIT: function based on current feedback:

      createSlot(tries) {
        const _this = this;
        return new Promise(function cb(resolve, reject) {
          console.log(`${tries} remaining`);
          if (--tries > 0) {
            setTimeout(() => {
              cb(resolve, reject);
            }, 500);
          } else {
            const { divId, adUnitPath } = _this;
            const { sizes } = _this.props;
    
            // if it's not, reject
            if (!_this.isPubadsReady()) {
              reject(new Error('pubads not ready'));
            }
            // if it's there resolve
            window.googletag.cmd.push(() => {
              const slot = window.googletag
                .defineSlot(adUnitPath, sizes, divId)
                .addService(window.googletag.pubads());
              resolve(slot);
            });
          }
        });
      }