【问题标题】:Chaining http calls in angular 2 in a for loop在for循环中以角度2链接http调用
【发布时间】:2017-07-12 16:29:08
【问题描述】:

我有一些看起来像

的代码
//service.ts

addProduct(productId) {
   this.http.post('someUrl', ReqData).map(json).subscribe(doStuff);
}

//component.ts

addAllproducts(productsIds) {
   productIds.forEach(productId => service.addProduct(productId);
}

我想要的是能够在调用下一个productId之前等待每个调用完成,而不使用window.setTimeout ..

【问题讨论】:

  • 您可以将所有产品作为产品 ID 列表/数组发送,然后在后端代码上进行实际添加
  • 很遗憾,我无法访问后端..
  • productIds.forEach(productId => service.addProduct(productId) 不是有效的方法。在一个请求中将所有产品发送到服务器
  • 看看 Angular 的承诺。这样,您可以链接您的请求并按顺序运行它们。它在 SO 上的几个问题中进行了描述,例如这里:stackoverflow.com/questions/25704745/…
  • @infamoustrey 或者只是将它们推入一个数组,通过push()ing 到 is 和 shift()ing 它。如果队列是干净的,它就会停止。

标签: javascript angular typescript ngrx


【解决方案1】:

使用.expand() 进行一些递归调用怎么样?

首先,创建一个递归函数并映射数据以供递归使用:

const recursiveAddProduct = (currentProductId, index, arr)=>{
    return service.addProduct(currentProductId)
        .map((response)=>{
            return {
                data:response,
                index: index+1,
                arr:arr
            }
        })
};

现在,在您的组件中递归调用它:

//productIds is an array of Ids    
//start of using the first index of item, where index = 0

let reduced = recursiveAddProduct(productIds[0],0,productIds)
    .expand((res)=>{
        return res.index>res.arr.length-1 ? Observable.empty(): recursiveAddProduct(productIds[res.index],res.index,productIds)
    });

reduced.subscribe(x=>console.log(x));

这是一个有效的JSBin

使用.expand操作符的好处:

  1. 您仍在使用 Observables,并且可以链接任何您想要的运算符。
  2. 您一个接一个地调用 http,这是您的要求。
  3. 您无需担心错误处理,它们都链接到单个流。只需将 .catch 呼叫您的 observables。
  4. 您可以对递归方法执行任何操作(数据操作等)
  5. 您可以设置终止递归调用的条件。
  6. 单行(几乎)代码。

编辑

如果您不喜欢内联三元组,可以使用.take() 运算符终止递归,如下所示:

let reduced = recursiveAddProduct(productIds[0],0,productIds)
    .expand(res=>recursiveAddProduct(productIds[res.index],res.index,productIds))
    .take(productIds.length)

工作JSBin

【讨论】:

  • 谢谢老板。我会看看它并报告给你。
  • @AngularDebutant 很高兴我能帮上忙
  • 快速提问:为什么不使用take(productIds.length) 而不是递归?
  • @callback 是的,这也可以,但你仍然需要递归。 .take() 指定 that 递归的终止条件。
  • 是的,它虽然会节省几行代码,并使代码易于理解 IMO :)
【解决方案2】:

首先从你的服务方法返回 observable:

addProduct(productId) {
   return this.http.post('someUrl', ReqData).map(json).subscribe(doStuff);
}

并使用递归函数并在 subscribe 回调中为数组中的每个项目调用它:

let loop = (id: number) => {
  service.addProduct(id)
    .subscribe((result) => {
      // This logic can be modified to any way you want if you don't want to mutate the `producIds` array
      if (productIds.length) {
        loop(productIds.shift())
      }
    })
}

loop(productIds.shift())

【讨论】:

  • forkJoin 不会这样做。他希望所有调用都是顺序的,而不是并行的。
  • 您是否删除了最后一行中的 productId?所以如果 productId 最初是 1,那么它将是 loop([]) ..
  • 我正在传递从数组中删除的项目。处理完所有项目后,有一个 if 检查以停止循环。就像我上面所说的,您可以通过多种方式实现该特定逻辑(使用计数器等)。你可以选择你想要的。
  • 哦,我明白了。谢谢先生。
【解决方案3】:

您可以使用 Observable.merge()。 试试类似的东西

addProduct(productId):Observable<Response> {
   return this.http.post('someUrl', productId);
}

addAllproducts(productsIds) {
   let productedsObservable:Observable<Response>[]=[];
   for(let productID in productsIds){
    this.productedsObservable.push(this.addProduct(productID));
   }
   return Observable.merge(productedsObservable)
}

您需要订阅请求的函数才能执行 http 请求。 你可以阅读更多关于组合运算符(如合并)here

【讨论】:

  • 这个答案听起来很棒!如果它有效,我会接受它,因为我喜欢它使用 Observables 运算符的方式。
  • 你那里的response类型是什么?
  • Response是http请求返回的对象类型。你可以在这里阅读它angular.io/api/http/Response
  • 好的!然后是 而不是 :)
  • 上述解决方案不适用于 RxJS v5+,因为合并运算符需要逗号分隔值。因此,要使其正常工作,请使用return Observable.merge(...productedsObservable)
猜你喜欢
  • 1970-01-01
  • 2015-09-10
  • 1970-01-01
  • 2013-12-07
  • 1970-01-01
  • 2013-12-26
  • 1970-01-01
  • 2018-01-25
  • 2021-01-02
相关资源
最近更新 更多