Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/399.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 用rxjs实现指数退避_Javascript_Angular_Typescript_Rxjs_Observable - Fatal编程技术网

Javascript 用rxjs实现指数退避

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

Angular 7提供了
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

    这里发生的事情是,我们简单地忽略了
    尝试触发的值,我们只关心它们被触发的事实。每当发生这种情况时,我们从
    范围
    可观察范围中提取下一个值

  • …把它摆正。这是指数退避的指数部分

    所以,现在无论何时(重新)订阅源失败,我们手中都有一个不断增加的整数(1,4,9,16…)。我们如何将该整数转换为直到下次重新订阅的时间延迟

    记住,我们当前使用的这个函数必须返回一个可观察的,使用
    尝试
    作为输入。这个可观察的结果只构建了一次
    retryWhen
    然后订阅结果可观测值,并且:每当结果可观测值触发时重试订阅源可观测值
    next
    ;每当产生的observable触发相应事件时,对SourceObservable调用
    complete
    error

  • 长话短说,我们需要让
    retryWhen
    稍等。可以使用操作符,但设置延迟的指数增长可能会很痛苦。相反,
    mergeMap
    操作符起作用

    mergeMap
    是两个运算符组合的快捷方式:
    map
    mergeAll
    <代码>映射
    只需将每一个递增的整数(1、4、9、16…)转换为一个
    计时器
    可观测值,在经过的毫秒数后触发
    下一个
    mergeAll
    强制
    retryWhen
    实际订阅
    timer
    。如果最后一位没有发生,我们得到的observable将立即触发
    next
    ,并将
    timer
    observable实例作为值

  • 此时,我们已经构建了定制的observable,它将被
    retryWhen
    用来决定何时尝试重新订阅源observable

  • 目前,我认为这一实施存在两个问题:

    • 一旦生成的observable发出最后一次
      next
      (导致最后一次尝试重新订阅),它也会立即发出
      complete
      。除非源observable很快返回结果(假设最后一次重试将成功),否则该结果将被忽略

      这是因为只要
      retryWhen
      从我们的观察对象中听到
      complete
      ,它就会调用源代码上的
      complete
      ,源代码可能仍在发出AJAX请求的过程中

    • 如果所有重试都失败,则源实际调用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); }) ); }