Javascript 用rxjs实现指数退避
Angular 7提供了Javascript 用rxjs实现指数退避,javascript,angular,typescript,rxjs,observable,Javascript,Angular,Typescript,Rxjs,Observable,Angular 7提供了rxjsObservables在实现AJAX请求的指数退避时的实际使用示例: import { pipe, range, timer, zip } from 'rxjs'; import { ajax } from 'rxjs/ajax'; import { retryWhen, map, mergeMap } from 'rxjs/operators'; function backoff(maxTries, ms) { return pipe( retryWh
rxjs
Observable
s在实现AJAX请求的指数退避时的实际使用示例:
import { pipe, range, timer, zip } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { retryWhen, map, mergeMap } from 'rxjs/operators';
function backoff(maxTries, ms) {
return pipe(
retryWhen(attempts => range(1, maxTries)
.pipe(
zip(attempts, (i) => i),
map(i => i * i),
mergeMap(i => timer(i * ms))
)
)
);
}
ajax('/api/endpoint')
.pipe(backoff(3, 250))
.subscribe(data => handleData(data));
function handleData(data) {
// ...
}
虽然我理解可观察性和退避的概念,但我不太清楚retryWhen
将如何准确地计算重新订阅源ajax
的时间间隔
具体地说,如何在这种设置中进行操作和工作
当尝试
对象被发射到retryWhen
时,它将包含什么
我浏览了他们的参考页,但仍然无法理解这一点。我已经花了相当长的时间研究这一点(出于学习目的),并将尽可能彻底地解释这段代码的工作原理 首先,这是原始代码,注释如下:
import { pipe, range, timer, zip } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { retryWhen, map, mergeMap } from 'rxjs/operators';
function backoff(maxTries, ms) { // (1)
return pipe( // (2)
retryWhen(attempts => range(1, maxTries) // (3)
.pipe(
zip(attempts, (i) => i), // (4)
map(i => i * i), // (5)
mergeMap(i => timer(i * ms)) // (6)
)
)
); // (7)
}
ajax('/api/endpoint')
.pipe(backoff(3, 250))
.subscribe(data => handleData(data));
function handleData(data) {
// ...
}
retryWhen
操作符创建自定义backoff
操作符。稍后我们将能够在管道
函数中应用这一点pipe
方法返回一个自定义运算符retryWhen
操作符。它接受一个函数参数。此函数将被调用一次,特别是在首次遇到/调用此retryWhen
时。顺便说一句,retryWhen
只有在源可观测对象产生错误时才起作用。然后,它会阻止错误进一步传播,并重新订阅源。如果源生成一个非错误结果(无论是在第一次订阅时还是在重试时),retryWhen
被传递且不涉及
关于尝试的几句话
。这是一个可观察的现象。它不是可观察到的源。它是专门为retryWhen
创建的。它有一个用途,只有一个用途:每当对源可观测对象的订阅(或重新订阅)导致错误时,尝试
触发一个下一个
。我们被给予尝试
,并且可以自由地使用它,以便以某种方式对每次失败的订阅尝试作出反应,从而获得可观测的源
这就是我们要做的
首先,我们创建范围(1,maxTries)
,这是一个可观测值,对于我们愿意执行的每次重试,它都有一个整数range
随时准备发射它的所有号码,但我们必须控制住它:我们只需要在再次重试时才需要一个新号码。所以,这就是为什么我们尝试将其压缩。也就是说,将每个发出的尝试值
与单个范围值
相结合
请记住,我们当前使用的函数只会被调用一次,并且在那个时候,尝试
只会为初始失败的订阅触发一次next
。因此,在这一点上,我们的两个压缩观测值只产生了一个值
顺便问一下,两个观测值压缩成一个是什么?此函数决定:(i)=>i
。为清晰起见,可以编写(itemFromRange,itemFromAttempts)=>itemFromRange
。第二个参数未使用,因此被删除,第一个参数被重命名为i
这里发生的事情是,我们简单地忽略了尝试触发的值,我们只关心它们被触发的事实。每当发生这种情况时,我们从范围可观察范围中提取下一个值
尝试
作为输入。这个可观察的结果只构建了一次retryWhen
然后订阅结果可观测值,并且:每当结果可观测值触发时重试订阅源可观测值next
;每当产生的observable触发相应事件时,对SourceObservable调用complete
或error
retryWhen
稍等。可以使用操作符,但设置延迟的指数增长可能会很痛苦。相反,mergeMap
操作符起作用
mergeMap
是两个运算符组合的快捷方式:map
和mergeAll
<代码>映射只需将每一个递增的整数(1、4、9、16…)转换为一个计时器
可观测值,在经过的毫秒数后触发下一个
mergeAll
强制retryWhen
实际订阅timer
。如果最后一位没有发生,我们得到的observable将立即触发next
,并将timer
observable实例作为值retryWhen
用来决定何时尝试重新订阅源observable- 一旦生成的observable发出最后一次
(导致最后一次尝试重新订阅),它也会立即发出next
。除非源observable很快返回结果(假设最后一次重试将成功),否则该结果将被忽略 这是因为只要complete
从我们的观察对象中听到retryWhen
,它就会调用源代码上的complete
,源代码可能仍在发出AJAX请求的过程中complete
- 如果所有重试都失败,则源实际调用
import { delay, dematerialize, map, materialize, retryWhen, switchMap } from "rxjs/operators"; import { concat, pipe, range, throwError, timer, zip } from "rxjs"; function backoffImproved(maxTries, ms) { return pipe( retryWhen(attempts => { const observableForRetries = zip(range(1, maxTries), attempts) .pipe( map(([elemFromRange, elemFromAttempts]) => elemFromRange), map(i => i * i), switchMap(i => timer(i * ms)) ); const observableForFailure = throwError(new Error('Could not complete AJAX request')) .pipe( materialize(), delay(1000), dematerialize() ); return concat(observableForRetries, observableForFailure); }) ); }