【问题标题】:Repeat a Promise until it's not rejected or reach a timeout [duplicate]重复一个承诺,直到它没有被拒绝或达到超时[重复]
【发布时间】:2017-02-24 03:47:35
【问题描述】:

我仍然是一个 Promise 菜鸟,并且正在尝试弄清楚如何让我的 Promise 重演。

有一个 ES6 承诺,如果没有设置一些全局标志,则拒绝。我需要它每 500 毫秒重试一次,直到:

  • 承诺返回一个解决方案,
  • 或达到最大尝试次数(比如说 10 次)。

由于 Promise 是异步的,我真的不想使用 setInterval() 检查,因为我认为异步代码不能正常工作。一旦承诺成功解决(或超时),我需要立即终止检查。

我正在使用 ES6 + React + ES6 Promises(请不要使用 Q 或 Bluebird 特定的答案!)

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'));

编辑:基于当前反馈的函数:

  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);
        });
      }
    });
  }

【问题讨论】:

  • 研究“递归函数”。基本上,在catch 中,我建议等待(通过setTimeout)一段时间,然后再次调用componentDidMount。你不需要做任何特殊的“终止”,因为如果 promise 解决了它就不会被调用。
  • 嗨@MikeMcCaughan,感谢您的帮助。所以我用这种方法看到的问题是,如果全局无法设置,它将永远调用。此外,在 React 中,componentDidMount 不应该被手动调用(尽管我们可以解决这个问题——我认为最大的问题是第一部分)
  • 是的,您只需要跟踪尝试次数。我想你可以弄清楚那部分:)。

标签: javascript promise ecmascript-6 es6-promise repeat


【解决方案1】:

正如 Mike McCaughan 所说,您可以使用 setTimeout 在尝试之间创建延迟。一旦您的承诺成功或尝试次数不多,请解决或拒绝您的承诺。

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'));

【讨论】:

  • 因为你提到了我的名字所以点赞:)。
  • 很好的答案!我想我很接近这个..在我看来,我会将我的解决/拒绝逻辑放在else 语句中?但这会导致尝试倒计时,并且只有一次tries !&gt;0 会执行解析/拒绝检查。不应该每次迭代都执行检查吗?
  • @MikeMcCaughan + Mike C 我已根据您的反馈将我的问题更新为我的功能的当前状态。
  • @Prefix 您的更新可能存在问题。您正在将resolve 添加到您推送到window.googletag.cmd 的匿名函数中。可能希望将其移至 推送之后。
  • 这是一个非常有用的答案。感谢你们解释的质量,以及你们俩的友好(这些天似乎很少见!)。干杯!
【解决方案2】:

你可以尝试链接调用,就像 promise 应该的那样,这有点做作,但我希望你明白我的意思:

PS 将全局对象附加到 windows 是一个坏主意,如果可能的话,不应该这样做,这只是展示了一个使用您的流程的快速示例...

window.attempts = 0;
window.maxAttempts = 10;
window.globalThing = true;
function 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!');
}).then((pass) => {
    return pass;
  }, (fail) => {
    window.attempts ++;
    //If within attempts, try again
    if(window.attempts < window.maxAttempts){
      //Chain a new promise to resolve with a timeout of 500ms
      return new Promise((resolve, reject) => {
         setTimeout(() => {
            resolve()
         }, 500);
      }).then(() => {
         //Then try again
         return createSlot();
      })
    }
    else {
      //else fail out with reason
      return fail;
    }
  });
}

【讨论】:

  • 嗨@longstaff——感谢您的帮助!这看起来像我正在尝试做的事情,尽管我如何在检查之间有延迟?这似乎会非常快速地执行所有 10 次尝试。我想在检查之间等待 500 毫秒左右(因为全局是由于加载异步脚本而设置的)
  • @Prefix 好的,所以用超时链接承诺,请参阅编辑
猜你喜欢
  • 2015-10-19
  • 1970-01-01
  • 2015-11-17
  • 2016-03-11
  • 2019-11-02
  • 2016-11-08
  • 1970-01-01
  • 2016-10-16
  • 2019-05-21
相关资源
最近更新 更多