Javascript 使用RXJS取消订阅

Javascript 使用RXJS取消订阅,javascript,rxjs,Javascript,Rxjs,我花了太多时间试图找出修复此嵌套订阅的最佳解决方案。我尝试了mergeMap、flatMap和switchMap,但没有成功。不幸的是,找到的例子并不完全是我所需要的,所以我只得到了一个结果,或者是一个未定义的结果,或者是一个错误。要修复的代码是: this.myService.getAll().subscribe(res => { // res.result is an array of 20 objects res.result.forEach(m => { // fo

我花了太多时间试图找出修复此嵌套订阅的最佳解决方案。我尝试了mergeMap、flatMap和switchMap,但没有成功。不幸的是,找到的例子并不完全是我所需要的,所以我只得到了一个结果,或者是一个未定义的结果,或者是一个错误。要修复的代码是:

this.myService.getAll().subscribe(res => {

// res.result is an array of 20 objects
res.result.forEach(m => {
    // for each of them I call 2 different endpoints adding a key with the response
    this.myService.checkFirst(m.id).subscribe(result => {
        m.first = result;
    });
    this.myService.checkSecond(m.id).subscribe(result => {
        m.second = result;
    });
});
// once all subscriptions are fulfilled I would like to return the mapped array
this.dataLength = res.total;
});
试一试

试一试


如果我正确理解你的问题,我会继续这样做

我假设this.myService.getAll是某种http调用,因此您希望在该调用返回且相关的可观察对象完成后立即执行某些操作。为此,操作员使用的是concatMap,它允许您在源对象this.myService.getAll(本例中为this.myService.getAll)完成后立即处理后续的观察对象

现在,检索this.myService.getAll的结果后,需要为返回的数组中的每个项发出2个调用。这样的调用可以并行运行,并且每个调用都有更新项目的某些属性的副作用

对于并行运行2个调用,可以使用forkJoin函数,该函数返回一个Observable,在两个调用完成后立即发出,并发出一个包含每个调用结果的数组。换句话说,这个被剪掉的代码应该只处理一个项目

forkJoin([this.myService.checkFirst(m.id), this.myService.checkSecond(m.id)]).pipe(
   tap(([first, second]) => {
     m.first = first;
     m.second = second;
   })
)
由于数组中有20个项,因此需要运行上述逻辑的20倍,可能是并行运行。如果是这种情况,您可以再次使用forkJoin为数组中的每个项运行上述请求

所以,将其缝合在一起,您的解决方案可能看起来像这样

this.myService.getAll().pipe(
  concatMap(res => {
    // reqestForItems is an array of Observables, each Observable created by calling the forkJoin that allows us to run the 2 calls in parallel
    const reqestForItems = res.result.map(m => 
      forkJoin([this.myService.checkFirst(m.id), this.myService.checkSecond(m.id)]).pipe(
        tap(([first, second]) => {
          m.first = first;
          m.second = second;
        })
      )
    )
    // return the result of the execution of requests for the items
    return forkJoin(reqestForItems).pipe(
      // since what is requested as result is the array with each item enriched with the data retrieved, you return the res object which has been modified by the above logic
      map(() => res)
    )
  })
)
.subscribe(res => // the res.result is an array of item where each item has been enriched with data coming from the service)

如果您必须处理Observables和http用例,您可能会发现很有趣。

如果我正确理解您的问题,我会继续这样做

我假设this.myService.getAll是某种http调用,因此您希望在该调用返回且相关的可观察对象完成后立即执行某些操作。为此,操作员使用的是concatMap,它允许您在源对象this.myService.getAll(本例中为this.myService.getAll)完成后立即处理后续的观察对象

现在,检索this.myService.getAll的结果后,需要为返回的数组中的每个项发出2个调用。这样的调用可以并行运行,并且每个调用都有更新项目的某些属性的副作用

对于并行运行2个调用,可以使用forkJoin函数,该函数返回一个Observable,在两个调用完成后立即发出,并发出一个包含每个调用结果的数组。换句话说,这个被剪掉的代码应该只处理一个项目

forkJoin([this.myService.checkFirst(m.id), this.myService.checkSecond(m.id)]).pipe(
   tap(([first, second]) => {
     m.first = first;
     m.second = second;
   })
)
由于数组中有20个项,因此需要运行上述逻辑的20倍,可能是并行运行。如果是这种情况,您可以再次使用forkJoin为数组中的每个项运行上述请求

所以,将其缝合在一起,您的解决方案可能看起来像这样

this.myService.getAll().pipe(
  concatMap(res => {
    // reqestForItems is an array of Observables, each Observable created by calling the forkJoin that allows us to run the 2 calls in parallel
    const reqestForItems = res.result.map(m => 
      forkJoin([this.myService.checkFirst(m.id), this.myService.checkSecond(m.id)]).pipe(
        tap(([first, second]) => {
          m.first = first;
          m.second = second;
        })
      )
    )
    // return the result of the execution of requests for the items
    return forkJoin(reqestForItems).pipe(
      // since what is requested as result is the array with each item enriched with the data retrieved, you return the res object which has been modified by the above logic
      map(() => res)
    )
  })
)
.subscribe(res => // the res.result is an array of item where each item has been enriched with data coming from the service)

如果您必须处理observables和http用例,您可能会发现很有趣。

RxJS的一个很好的特性是,您可以将流嵌套到任意深度。因此,如果您可以构建一个流来丰富单个对象,那么您可以嵌套其中的20个流来丰富整个数组

因此,对于一个强化对象,将强化对象打印到控制台的流可能如下所示:

const oneObject = getObject();
forkJoin({
  firstResult: this.myService.checkFirst(oneObject.id),
  secondResult: this.myService.checkSecond(oneObject.id)
}).pipe(
  map(({firstResult, secondResult}) => {
    oneObject.first = firstResult;
    oneObject.second = secondResult;
    return oneObject;
  })
).subscribe(
  console.log
);
this.myService.getAll().pipe(
  map(allRes =>
    allRes.result.map(m => 
      forkJoin({
        first: this.myService.checkFirst(m.id),
        second: this.myService.checkSecond(m.id)
      }).pipe(
        map(({first, second}) => {
          m.first = first;
          m.second = second;
          return m;
        })
      )
    )
  ),
  // forkJoin our array of streams, so that your 40 service calls (20 for 
  // checkFirst and 20 for checkSecond) are all combined into a single stream.
  mergeMap(mArr => forkJoin(mArr)),
).subscribe(resultArr => {
  // resultArr is an aray of length 20, with objects enriched with a .first
  // and a .second
  // Lets log the result for he first object our array.
  console.log(resultArr[0].first, resultArr[0].second)
});
如果一个对象本身是从一个可观察对象返回的,那么同样的事情会是什么样子?这是同样的事情,只是现在我们合并或切换我们的对象到我们上面创建的相同流中

this.myService.getOneObject().pipe(
  mergeMap(oneObject => 
    forkJoin({
      firstResult: this.myService.checkFirst(oneObject.id),
      secondResult: this.myService.checkSecond(oneObject.id)
    }).pipe(
      map(({firstResult, secondResult}) => {
        oneObject.first = firstResult;
        oneObject.second = secondResult;
        return oneObject;
      })
    )
  )
).subscribe(
  console.log
);
现在,只剩下一步了。要对整个对象数组执行这一切。为了实现这一点,我们需要一种方法来运行一组可观测数据。幸运的是,我们有forkJoin——我们用来同时运行checkFirst和checkSecond的同一个操作符。它也可以将整个事情连接在一起。可能是这样的:

const oneObject = getObject();
forkJoin({
  firstResult: this.myService.checkFirst(oneObject.id),
  secondResult: this.myService.checkSecond(oneObject.id)
}).pipe(
  map(({firstResult, secondResult}) => {
    oneObject.first = firstResult;
    oneObject.second = secondResult;
    return oneObject;
  })
).subscribe(
  console.log
);
this.myService.getAll().pipe(
  map(allRes =>
    allRes.result.map(m => 
      forkJoin({
        first: this.myService.checkFirst(m.id),
        second: this.myService.checkSecond(m.id)
      }).pipe(
        map(({first, second}) => {
          m.first = first;
          m.second = second;
          return m;
        })
      )
    )
  ),
  // forkJoin our array of streams, so that your 40 service calls (20 for 
  // checkFirst and 20 for checkSecond) are all combined into a single stream.
  mergeMap(mArr => forkJoin(mArr)),
).subscribe(resultArr => {
  // resultArr is an aray of length 20, with objects enriched with a .first
  // and a .second
  // Lets log the result for he first object our array.
  console.log(resultArr[0].first, resultArr[0].second)
});
以下是相同的解决方案,我将地图和合并地图折叠为一个合并地图:

如果您不确定checkFirst和checkSecond是否已完成,可以使用zip代替forkJoin,然后使用take1或first取消订阅


RxJS的一个很好的特性是,您可以将流嵌套到任意深度。因此,如果您可以构建一个流来丰富单个对象,那么您可以嵌套其中的20个流来丰富整个数组

因此,对于一个强化对象,将强化对象打印到控制台的流可能如下所示:

const oneObject = getObject();
forkJoin({
  firstResult: this.myService.checkFirst(oneObject.id),
  secondResult: this.myService.checkSecond(oneObject.id)
}).pipe(
  map(({firstResult, secondResult}) => {
    oneObject.first = firstResult;
    oneObject.second = secondResult;
    return oneObject;
  })
).subscribe(
  console.log
);
this.myService.getAll().pipe(
  map(allRes =>
    allRes.result.map(m => 
      forkJoin({
        first: this.myService.checkFirst(m.id),
        second: this.myService.checkSecond(m.id)
      }).pipe(
        map(({first, second}) => {
          m.first = first;
          m.second = second;
          return m;
        })
      )
    )
  ),
  // forkJoin our array of streams, so that your 40 service calls (20 for 
  // checkFirst and 20 for checkSecond) are all combined into a single stream.
  mergeMap(mArr => forkJoin(mArr)),
).subscribe(resultArr => {
  // resultArr is an aray of length 20, with objects enriched with a .first
  // and a .second
  // Lets log the result for he first object our array.
  console.log(resultArr[0].first, resultArr[0].second)
});
如果一个对象本身是从一个可观察对象返回的,那么同样的事情会是什么样子?这是同样的事情,只是现在我们合并或切换我们的对象到我们上面创建的相同流中

this.myService.getOneObject().pipe(
  mergeMap(oneObject => 
    forkJoin({
      firstResult: this.myService.checkFirst(oneObject.id),
      secondResult: this.myService.checkSecond(oneObject.id)
    }).pipe(
      map(({firstResult, secondResult}) => {
        oneObject.first = firstResult;
        oneObject.second = secondResult;
        return oneObject;
      })
    )
  )
).subscribe(
  console.log
);
现在,只剩下一步了。要对整个对象数组执行这一切。为了实现这一点,我们需要一种方法来运行一组可观测数据。幸运的是,我们有forkJoin——我们用来同时运行checkFirst和checkSecond的同一个操作符。它也可以将整个事情连接在一起。可能是这样的:

const oneObject = getObject();
forkJoin({
  firstResult: this.myService.checkFirst(oneObject.id),
  secondResult: this.myService.checkSecond(oneObject.id)
}).pipe(
  map(({firstResult, secondResult}) => {
    oneObject.first = firstResult;
    oneObject.second = secondResult;
    return oneObject;
  })
).subscribe(
  console.log
);
this.myService.getAll().pipe(
  map(allRes =>
    allRes.result.map(m => 
      forkJoin({
        first: this.myService.checkFirst(m.id),
        second: this.myService.checkSecond(m.id)
      }).pipe(
        map(({first, second}) => {
          m.first = first;
          m.second = second;
          return m;
        })
      )
    )
  ),
  // forkJoin our array of streams, so that your 40 service calls (20 for 
  // checkFirst and 20 for checkSecond) are all combined into a single stream.
  mergeMap(mArr => forkJoin(mArr)),
).subscribe(resultArr => {
  // resultArr is an aray of length 20, with objects enriched with a .first
  // and a .second
  // Lets log the result for he first object our array.
  console.log(resultArr[0].first, resultArr[0].second)
});
这是我折叠地图的相同解决方案 并将地图合并到单个合并地图中:

如果您不确定checkFirst和checkSecond是否已完成,可以使用zip代替forkJoin,然后使用take1或first取消订阅


呵呵。我只是写出了基本相同的解决方案。只是我不认为点击来充实对象,然后点击map=>obj将充实对象推送到流中是非常干净的,尽管它可以工作!。我使用map并返回m,而不是tap;在最后一行。那么第二个forkJoin根本不需要管道。我只是写出了基本相同的解决方案。只是我不认为点击来充实对象,然后点击map=>obj将充实对象推送到流中是非常干净的,尽管它可以工作!。我使用map并返回m,而不是tap;在最后一行。然后,第二个forkJoin根本不需要管道。我认为这比一次执行所有调用的速度慢一倍。这是因为在运行checkSecond之前,您必须等待checkFirst完成。我认为这比一次执行所有调用的速度慢一倍。这是因为在运行checkSecond之前,必须等待checkFirst完成。