6

I have the following service calls available:
productService.GetAllProducts()
productService.DeleteProduct()
productService.GetCategories()
productService.DeleteCategory()

In sudo code I need to do the following in my component:

  1. Get a list of products using productService.GetAllProducts().

  2. Loop through the list of products and call productService.DeleteProduct() for each product.

  3. Once I can confirm the above deletes are all complete (due to db constraints) I need to then get a list of categories using productService.GetCategories(). Loop through each category and call productService.DeleteCategory().

I am aware that my life would be a lot easier if I had better backend calls to do bulk deletes, but I do not have a choice in this case. I need to follow the pattern of getting a list, looping through it, doing an individual delete one each item.

Is it even possible doing what I am trying to do using flatMap and the observable complete param? My biggest problem is knowing when the code is finished deleting all of the products before searching for and deleting all of the categories.

Blake Rivell
  • 13,105
  • 31
  • 115
  • 231
  • You already asked the same question: https://stackoverflow.com/questions/51047390/guarantee-observable-subscription-finishes-before-proceeding-to-next-code. And you got an answer and several links. – JB Nizet Jun 26 '18 at 17:50
  • 1
    The links in that post do not solve my problem. Are you actually reading what I am trying to do or just instantly marking things as duplicate based on the title? I realized my problem is slightly different from the post you linked so created a new post. – Blake Rivell Jun 26 '18 at 17:56
  • They do solve your problem. But observables are hard, and you need to read the articles carefully, multiple times if needed, and experiment before simply rejecting the solution. If there is something you don't understand in the articles / suggestions, then ask for clarifications instead of reposting the same question. – JB Nizet Jun 26 '18 at 17:58
  • Got it I will read into them more thoroughly. Is there anyway you can just give me a general idea looking at my 4 sudo code steps above what functions I am going to need for each part? – Blake Rivell Jun 26 '18 at 18:01
  • 1
    Make a single observable out of the several ones that need to be executed in parallel (i.e. the many deletions), using forkJoin. Use switchMap to execute one observable after another. – JB Nizet Jun 26 '18 at 18:02
  • Thank you, that should get me on the right path. I apologize, I didn't realize observables are that complicated. I am so used to writing code like this synchronously in c# just making direct calls to the repositories. – Blake Rivell Jun 26 '18 at 18:06
  • @JBNizet that is *not* what switchMap does! – cwharris Jun 26 '18 at 19:41
  • @BJNizet At the very least, that is an incomplete misleading description of `switchMap`. `switchMap` only maintains a single internal subscription, and will cancel all but the latest subscription if the subscription has not already completed. `concat` subscribes to one observable after the other completes. `switchMap` subscribes as soon as the next observable is available, and immediately disposes of it's subscription to the previous observable. – cwharris Jun 26 '18 at 19:44

1 Answers1

7

You may want to try something along these lines

productService.GetAllProducts()
.switchMap(
   products => forkJoin(products.map(product => productService.DeleteProduct(product)))
)
.switchMap(() => productService.GetCategories())
.switchMap(
   categories => forkJoin(categories.map(category => productService.DeleteCategory(category)))
)
.subscribe(() => console.log('done'))

The whole idea is the following

  • GetAllProducts returns an array of Products which is passed as parameter to the first switchMap
  • The Products array is transformed, via map, into an array of Observables which are the result of DeleteProduct - the array of Observable is passed to the first forkJoin as its parameter
  • forkJoin emits when all the Observables it has received as parameter complete, and therefore will emit when all the Products have been deleted
  • The same reasoning is repeated for categories

I am not sure the code is syntactically perfect, but it should be enough to give you an idea on how to proceed.

Picci
  • 16,775
  • 13
  • 70
  • 113
  • thank you for the excellent answer. I actually ended up resolving it in a similar way still using forkJoin and map for the deletes so they happen in parallel, but set each of the delete observables to a variable and then used concat and subscribed to it. Kind of like this... Subscribe to GetAllProducts then within that create a const deleteProducts that does the forkJoin and map. Then subscribed to GetCategories then within that create a const deleteCategories that does the forkJoin and map. Then finally const deletes = concat(deleteProducts, deleteCategories).subscribe(). – Blake Rivell Jun 27 '18 at 18:43
  • let me know if you feel like the switchMap technique is better for any certain reason. – Blake Rivell Jun 27 '18 at 18:44
  • I am not sure I understand the logic, but at a first glance I question how you are sure that `deletes = concat(deleteProducts, deleteCategories).subscribe()` runs with `deleteProducts` and `deleteCategories` are actually variables pointing to the result of the 2 `forkJoin` and they are not still `undefined`. The approach I am suggesting is a chain of operators transforming the first "source" Observable into other Observables, step by step, until you get the last one to which you subscribe to get stuff done. Generally speaking I tend to think that the less you `subscribe` the better it is. – Picci Jun 27 '18 at 20:00
  • With the chain I am suggesting you always know what you are doing and, somehow, you are able to get back in control of streams of asynchronous events, such the ones you are describing. – Picci Jun 27 '18 at 20:02