【问题标题】:How to run http put request in a loop and get status of every individual request如何在循环中运行 http put 请求并获取每个请求的状态
【发布时间】:2020-07-13 06:52:29
【问题描述】:

我有一个对象数组,它的每个对象都需要通过 put http 请求在数据库中更新。

[
{id: "3e81731f-e3b4-405d-a5ca-bef52c1b036a", date_created: "2019-07-24T15:55:31.372460Z", date_modified: "2020-07-13T03:25:02.720870Z", einsight_uuid: "d15176ab-ecf8-dba1-e040-10ac316407fa", attributes: {…}},
{id: "4707bff2-8265-456f-a4d4-ca1134d85620", date_created: "2019-07-24T15:55:31.372460Z", date_modified: "2020-07-13T03:25:06.238019Z", einsight_uuid: "fcda4259-3ecb-4566-85b0-5b6d5c7647f6", attributes: {…}}
{id: "d29a3340-04b6-431b-8671-a0f0d25a9b51", date_created: "2019-07-24T15:55:31.3724", date_modified: "2020-07-13T03:25:06.238019Z", einsight_uuid: "fcda4259-3ecb-4566-85b0-5b6d5c7647f6", attributes: {…}}
]

如何调用 api 将每条记录保存在数组中。在循环中调用 Api 是一个简单的选择,但还不够。我想记录每个响应。其中有多少成功或失败。 是否有任何已知的最佳实践可以在 Angular 或 rxjs 中执行此类操作

【问题讨论】:

  • 每次调用你的 api 你都会得到一个observable 响应,你可以.subscribe()。在这些subscribe 回调中,您将获得每个请求的响应。这符合您的要求吗?
  • 是的,我可以这样做。我可以订阅响应并使用每个记录状态更新用户。那么在循环中调用放置请求完全可以吗?没有性能问题?我想知道是否有像我们有 ForkJoin 的东西可用于 put

标签: angular rxjs httprequest angular-services


【解决方案1】:

您可以根据请求的数量和您的要求以并行或顺序请求的方式执行此操作。

并行请求

将 RxJS forkJoin 函数与 tapcatchError 运算符一起使用。试试下面的

import { forkJoin, of, from } from 'rxjs';
import { tap, catchError, concatMap } from 'rxjs/operators';

putDataParallel() {
  let success = 0;                   // <-- trivial counters
  let errors = 0;

  const reqs = this.urls.map(url =>  // <-- replate `this.urls` with your object array
    this.http.put(url).pipe(         // <-- replace `url` with your own PUT request
      tap(_ => success++),           // <-- count successful responses here
      catchError(err => {        
        errors++;                    // <-- count errors here
        return of(err);              // <-- remember to return an observable from `catchError`
      })
    )
  );

  forkJoin(reqs).subscribe(
    null,                            // <-- you could change response callback to your requirement
    err => console.log(err),
    () => console.log(`Success: ${success}\nErrors: ${errors}`),
  );
}

顺序请求

使用 RxJS from 函数和 concatMap 操作符来处理顺序数据流。试试下面的

import { forkJoin, of, from } from 'rxjs';
import { tap, catchError, concatMap } from 'rxjs/operators';

putDataSequential() {
  let success = 0;                      // <-- trivial counters
  let errors = 0;

  from(this.urls).pipe(                 // <-- replate `this.urls` with your object array
    concatMap(url => {
      return this.http.put(url).pipe(   // <-- replace `url` with your own PUT request
        tap(_ => success++),            // <-- count successful responses here
        catchError(err => {        
          errors++;                     // <-- count errors here
          return of(err);               // <-- remember to return an observable from `catchError`
        })
      )
    })
  ).subscribe(
    null,                               // <-- you could change response callback to your requirement
    err => console.log(err),
    () => console.log(`Success: ${success}\nErrors: ${errors}`),
  );
}

无论错误或响应如何,这两种方法都将一直运行,直到数组中的所有对象都完成为止。但是,如果您希望在出现错误时中断序列,请替换 catchError 运算符中的 return of(err); 语句以引发 errorcomplete 通知。例如,您可以使用 RxJS EMPTY 常量:return EMPTY;

我以微不足道的计数器为例。您可以改为使用对象(例如)来记录响应/错误的数量以及 HTTP 请求的相应输入。

工作示例:Stackblitz

【讨论】:

    【解决方案2】:

    你可以使用 rxjs concat,但是 concat 会在 observable 抛出错误时停止,所以你可以使用 catchError 返回一个新的 observable

    import { of ,concat} from 'rxjs';
    ....
     data = [
        {id:1},
        {id:2},
        {id:-1},
        {id:2},
    
        ]
      constructor(private http:HttpClient){
        let httpReqs = this.data
              .map(i => 
                  this.http.put(`https://jsonplaceholder.typicode.com/posts/${i.id}`,{})
                  .pipe(catchError(err => of({err})))
                  );
    
         concat(...httpReqs).subscribe(console.log)
        }
    

    demo ??

    检查这个?rxjs concat

    【讨论】:

    • 感谢您的回答,但我不想停止它,直到它完成所有记录。
    • concat 将在出现错误时停止,但我使用 catchError 返回一个新的 observable,在这种情况下下一个 observable 将运行,检查演示控制台
    【解决方案3】:

    如您所见,每个项目都将使用 concateMap 进行处理,并且在发出HTTP 请求后,每个项目resultarray 将被更新。 有多种方法,你可以达到这个结果。这是此处显示的方法之一。

     items =[
           {id: "3e81731f-e3b4-405d-a5ca-bef52c1b036a", name :'united', result:[]},
           {id: "4707bff2-8265-456f-a4d4-ca1134d85620", name: 'ind', result:[]},
           {id: "d29a3340-04b6-431b-8671-a0f0d25a9b51", name:'ru', result:[]},
           {id: "xxx-xxxx-xxxx-xxxxx", name:'', result:[]}
        ]
    
    
    
    from(this.items)
        .pipe(
            concatMap(item => this.ob2(item.name).pipe(
              catchError(err=>of(err)),
              tap(result =>{
                if(result.length){
                  item.result = result;
                }else{
                  // error scenario can be handled here
                }
                console.log('Updated each item', item)
              })
            ))
        ).subscribe();
      }
    

    DEMO

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-09
      • 1970-01-01
      • 2019-07-22
      • 2014-09-25
      • 2018-12-19
      • 2014-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多