在Angular 2中使用for循环链接http调用

6

我有一些类似这样的代码:

//service.ts

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

//component.ts

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

我希望您能在不使用window.setTimeout的情况下,等待每个调用完成后再调用下一个productId。

1
你可以将所有产品作为产品ID的列表/数组发送过来,然后在后端代码中进行实际添加。 - DDelgro
productIds.forEach(productId => service.addProduct(productId) 这种方式不是很高效。建议将所有产品一次性发送到服务器。 - Hadi Farhadi
1
看一下Angular的Promise。这样你就可以链接你的请求并按顺序运行它们。在SO上有几个问题描述了这个,例如这里:https://dev59.com/P18e5IYBdhLWcg3wfaUy - qqilihq
使用回调函数,通过简单的计数器跟踪已处理的productIDs数量,在回调函数中递减计数器。 - infamoustrey
1
@infamoustrey 或者将它们推入数组中,通过 push()shift() 实现。如果队列干净,则停止。 - Ismael Miguel
显示剩余3条评论
3个回答

7
如何使用.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. 不需要担心错误处理,它们都链接到单个流中。只需在你的Observables中调用.catch即可。
  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
@AngularDebutant 很高兴我能帮到你。 - CozyAzure
1
快速问题:为什么你没有使用 take(productIds.length) 而是使用递归? - callback
@callback 是的,那也可以,但你仍然需要递归。.take() 指定了 递归的终止条件。 - CozyAzure
是的,这样做可以节省几行代码,并且在我看来可以使代码易于理解 :) - callback
@callback 合法。已添加编辑。我只是太习惯三元运算符了 :) - CozyAzure

3

首先从你的服务方法中返回可观察对象:

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不行。他希望所有的调用都是按顺序进行的,而不是同时进行的。 - CozyAzure
你在最后一行不是要删除productId吗?所以如果productId最初为1,那么它将变成loop([]).. - AngularDebutant
我正在传递从数组中删除的项目。当所有项目都被处理时,有一个if检查来停止循环。如我上面所说,您可以使用许多方式实现特定的逻辑(使用计数器等)。您可以自行选择。 - Saravana
哦,我明白了。谢谢您先生。 - AngularDebutant

0
你可以使用 Observable.merge()。 试试这样做。
addProduct(productId):Observable<Response> {
   return this.http.post('someUrl', productId);
}

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

您需要订阅所请求的函数,以便执行HTTP请求。 您可以在这里阅读有关组合运算符(如合并)的更多信息。


这个回答听起来很棒!如果它能用,我会接受它,因为我喜欢它如何使用Observables操作符。 - AngularDebutant
你那里的 response 类型是什么? - AngularDebutant
响应(Response)是 HTTP 请求返回的对象类型。您可以在此处阅读有关它的信息:https://angular.io/api/http/Response - Gili Yaniv
好的!那么它是<Response>而不是<response> :) - AngularDebutant
以上解决方案对于 RxJS v5+ 无效,因为合并操作符预计是逗号分隔的值。因此,要使其有效,请使用 return Observable.merge(...productedsObservable) - Koushik Ravulapelli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接