简体   繁体   English

来自嵌套 observables 的 Angular RXJS 轮询

[英]Angular RXJS Polling from within nested observables

I have a service which is used by a resolver to generate and return a report.我有一个由解析器用来生成和返回报告的服务。

The initial get in the service calls a REST endpoint /report which kicks off a worker job on the server,as the report is processor intensive and takes over 30 seconds to run.服务中的初始获取调用 REST 端点/report ,它启动服务器上的工作作业,因为报告是处理器密集型的,运行时间超过 30 秒。 The report endpoint returns the id of the worker job. report端点返回工作作业的 ID。

I then need to poll the workder job REST endpoint /job/job_id with the relevant id for the job.然后,我需要使用作业的相关 id 轮询工人作业 REST 端点/job/job_id I continue to polling untill it returns a state 'completed', and contains the finished report.我继续轮询,直到它返回“已完成”状态,并包含完成的报告。

This final output is then returned from the service and the resolver consumes it.这个最终输出然后从服务返回,解析器使用它。

I have been unable to get this working with the polling.我一直无法通过投票来解决这个问题。 I pipe the response to the initial report endpoint into a switchMap and then use an interval to repeatedly poll every 500ms the /job/job_id endpoint.我将初始报告端点的响应通过管道传输到 switchMap 中,然后使用间隔每 500 毫秒重复轮询/job/job_id端点。 I then try and switchMap the poll repsonse and return if complete.然后我尝试切换映射投票响应并在完成时返回。 This is my first time using switchMap and polling, so i'm not sure if i am using this correctly.这是我第一次使用 switchMap 和轮询,所以我不确定我是否正确使用它。

Here is my latest attempt of the code:这是我对代码的最新尝试:

getDepartmentReport() {

  return this.http
    .get<any>(reportUrl, this.getAuthOptions(true))
    .pipe(switchMap(initialResponse => {

      interval(500).pipe(
        switchMap(() => {
          return this.http.get<any>(workerUrl + initialResponse.id, this.getAuthOptions(true))
            .pipe(
              switchMap(pollResponse => {
                if(pollResponse.state === 'completed') {
                  return pollResponse;
                }
            })
      }));
  }));
}

This in fact won't compile.这实际上不会编译。 It gives the following error:它给出了以下错误:

Argument of type '(initialResponse: any) => void' is not assignable to parameter of type '(value: any, index: number) => ObservableInput<any>'.
  Type 'void' is not assignable to type 'ObservableInput<any>'.

56         .pipe(switchMap(initialResponse => {

I assume this is happening because upon non complete polling responses, there is no return statement to handle that situation and a void is returning.我认为这是因为在不完整的轮询响应时,没有返回语句来处理这种情况,并且返回一个空值。

Anybody got any ideas?有人有任何想法吗? I'm stumped.我难住了。

This is an interesting problem.这是一个有趣的问题。

You're getting that error because switchMap must return an Observable .您收到该错误是因为switchMap必须返回一个Observable In your code, you're not returning anything, you just start an interval.在你的代码中,你没有返回任何东西,你只是开始一个间隔。

You also have to tell your interval when to stop polling.您还必须告诉您的时间间隔何时停止轮询。 This can be achieved with the help of the takeWhile operator.这可以在takeWhile运算符的帮助下实现。 In order to separate things a bit more, I've created a custom operator in which the polling will take place.为了将事情分开一点,我创建了一个自定义运算符,轮询将在其中进行。 Doing things this way, you can reuse this operator in other places as well.这样做,您也可以在其他地方重用此运算符。

Here is my approach:这是我的方法:

// ===== Server =====

let crtReportId = 1;
let crtReportStatus: { status: string, id: number };

const getReportFromBE = () => {
  let initialId = crtReportId;

  crtReportStatus = { status: 'pending', id: initialId };

  // It takes some time...
  timer(2000)
    .subscribe(() => crtReportStatus = { status: 'completed', id: initialId })

  return of(crtReportId++);
}

const getWorkerStatus = id => of(crtReportStatus);

// ===== Client =====

type CustomPollOperator = (data: any, cond: (d) => boolean, ms: number) => Observable<any>

const pollFor: CustomPollOperator = (data, cond, ms) => {
  let shouldPoll = true;

  return interval(ms)
    .pipe(
      tap(() => console.warn('pooling', shouldPoll)),
      takeWhile(() => shouldPoll),
      switchMap(() => getWorkerStatus(data)),
      tap(res => {
        if (cond(res)) {
          shouldPoll = false;
        }
      })
    )
}

const isWorkerCompleted = w => w.status === 'completed';

const getReports = () => {
  return getReportFromBE()
    .pipe(
      switchMap(workerId => pollFor(workerId,isWorkerCompleted, 200))
    )
}

getReports().subscribe((res) => console.log('result', res))

StackBlitz . 堆栈闪电战

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM