如何延迟重试以使用RxJS5发送HTTP请求?

时间:2016-12-23 15:32:41

标签: javascript reactive-programming rxjs5 reactive-extensions-js reactive

我遇到了使RxJS5可观察流以我想要的方式运行的问题。

流应该使用axios向网站发送HTTP请求,如果响应是HTTP错误(axios强制导致JavaScript错误),则可观察序列应等待10毫秒,然后尝试重新发送请求(由于某种原因,我发送请求的网站在您重试立即发送请求时不喜欢它并且不断抛出错误,但大多数情况下延迟10毫秒表现良好。)

Rx.Observable
  .fromPromise(axios('http://example.com/12345'))
  .map(x => new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(x)
    }, 2000)
  }))
  .debounceTime(2000)
  .do(console.log)
  .retry(10)
  .subscribe(console.log, console.error)

我在Codepen上有一些示例,只是做了一些更改,以便更明显地说明流的工作原理:http://codepen.io/leakyabstractions/pen/pNmvyZ?editors=0010

我尝试在.delay()运算符的位置使用.debounceTime().timer().timeInterval().timeout().map(),但没有(包括.map())有效。我做错了什么?

2 个答案:

答案 0 :(得分:1)

所以基本上你要找的是“10ms后重试,但只有10次”? (这是retry(10)建议的内容。 我认为一个复杂的解决方案包括retryWhen

const mockedRestError$ = Rx.Observable.throw("http://example.com/12345");

// we'll start with an empty string, because otherwhise
// we could not log the "start..."
Rx.Observable.of("")
  .do(() => console.log("start..."))
  .switchMapTo(mockedRestError$)
  .retryWhen(tenTimesWithDelay)
  .subscribe(console.log, console.error, console.info); // is never called, because 


function tenTimesWithDelay(errors) {
  return errors
    .scan((count, err) => {
      ++count;
      // optionally to throw the error all the way down to the subscription
      // comment the throw out to have the stream simply complete
      if (count >= 10) {
        throw err;
      }
      return count;
    }, 0)
    .takeWhile(count => count < 10)
    .do(count => console.log(`Retry #${count} in 100ms...`))
    .delay(100);
}

以下是代码笔:http://codepen.io/anon/pen/bBydwZ?editors=0010

请注意,我将延迟设置为100毫秒而不是10毫秒,这样它在控制台中显示出一点清洁。

答案 1 :(得分:-1)

olsn的回答有效,但是我想分享另一个我意外提出的解决方案,我觉得这更直接:

console.log('start')
Rx.Observable
// emit observable every 10 ms, change to a bigger number to observe results
.interval(10)
// start immediately
.startWith(0)
// do it 10 times
.take(10)
.do(x => console.log('emitting', x))
// for every observable emitted, do an HTTP request
.flatMap(() => new Promise((resolve, reject) => resolve('resolved promise')))
.first(x => !(x instanceof Error))
.subscribe(console.log, console.warn, () => console.info('done'))