1

With the following code I'm intending to subscribe to function1, and then depending on the result (saleAgreed), subscribe to a second observable; either function2 or of(undefined). See below:

this.apiService.function1(id).pipe(
        switchMap(async (saleAgreed: boolean) => {

          // Wait for some input from the user
          let cancelAgreedSale = await this.promptSaleAgreedCancelDetails();

          // These are both evaluating to true   
          console.log(`sale agreed: ${saleAgreed}`)
          console.log(`cancelSaleAgreed: ${cancelAgreedSale}`)

          iif(
            function() {
              if (saleAgreed && cancelAgreedSale) { 
                this.removeSaleAgreedRequest = { 
                  contactId : this.cid, 
                  details : this.cancelSaleAgreedDetails
                };
              }
              return saleAgreed && cancelAgreedSale;
            }, this.apiService.function2(this.removeSaleAgreedRequest), of(undefined));

          // Thought returning the observable might help, but doesn't make a difference whether 
          // it's here or not
          return this.apiService.function2(this.removeSaleAgreedRequest)
        })
      ).subscribe(res => { 
        // Returns either undefined or an observable if I return one above
        console.log(res)
      }, error => { 
        console.log(error.error)
      })

function2 implementation:

public function2(request: RemoveSaleAgreedRequest): Observable<any>  { 
    console.log('api func hit!')
    return this.http.patch<any>(this.apiUrl + 'some/endpoint', request);
  }

It's my understanding that my code should evaluate the result of the anonymous function I provide to iif(). If it evaluates to true, function2 should be subscribed to, otherwise it should subscribe to of(undefined). function1 is subscribed to fine, however even though my anonymous function is evaluating to true, function2 is not being subscribed to. What am I doing wrong? Thanks!

Loz
  • 118
  • 11

1 Answers1

2

You can't just call the iif operator like a function. iif returns an Observable which needs to be subscribed, ideally by an operator within your pipe.

Marking the callback as async will (probably) also cause problems. I don't think returning an Observable wrapped inside a Promise will be properly handled by RxJS. Mixing async and Observables is often not necessary. Let's wrap the call to this.promptSaleAgreedCancelDetails() into a from().

We can process result of this.promptSaleAgreedCancelDetails() in a switchMap. This switchMap logs the two variables and uses a normal if/else to handle the logic.

this.apiService
  .function1(id)
  .pipe(
    switchMap((saleAgreed: boolean) => {
      return from(this.promptSaleAgreedCancelDetails()).pipe(
        switchMap((cancelAgreedSale) => {
          console.log(`sale agreed: ${saleAgreed}`);
          console.log(`cancelSaleAgreed: ${cancelAgreedSale}`);

          if (saleAgreed && cancelAgreedSale) {
            this.removeSaleAgreedRequest = {
              contactId: this.cid,
              details: this.cancelSaleAgreedDetails,
            };
            return this.apiService.function2(this.removeSaleAgreedRequest)
          } else {
            return of(undefined)
          }
        })
      );
    })
  )
  .subscribe(
    (res) => console.log(res),
    (error) => console.log(error.error)
  );
Tobias S.
  • 21,159
  • 4
  • 27
  • 45
  • Hey, thanks for your response! I provided the async callback function in order to await input from the user - as `function2` depends on this input. If I change my code to what you've described here I get the error `Argument of type '() => Promise' is not assignable to parameter of type '() => boolean'.` on my anon function. If I then change `this.promptSaleAgreedCancelDetails()` to not be async it removes the error but `cancelAgreedSale` will evaluate to false since the user has not given their input yet, and thus `function2` will not be called. Do you know of a way around this? – Loz Jan 11 '23 at 15:38
  • @Laurence - Can you try replacing `of()` with `from()` (check my edit)? And always remove the `async`. – Tobias S. Jan 11 '23 at 15:41
  • Doesn't seem to work unfortunately - for clarity `this.promptSaleAgreedCancelDetails()` remains an async function, but when switching to `from()` `function2` isn't called. – Loz Jan 11 '23 at 15:57
  • What's the output of the two console.logs when using `from`? – Tobias S. Jan 11 '23 at 15:59
  • `sale agreed: true cancelSaleAgreed: true` – Loz Jan 11 '23 at 16:03
  • Ok, that's good. Does `apiService.function2` return an Obserable or a Promise? – Tobias S. Jan 11 '23 at 16:05
  • Returns an Observable :) Also - when I changed `this.promptSaleAgreedCancelDetails()` to not be async in my first comment- that `function2` was hit perfectly fine, just not with the correct data, since it hadn't been taken from the user yet (not sure if that might help) – Loz Jan 11 '23 at 16:06
  • can you share the implementation of `function2`? How do you detect if it was called? – Tobias S. Jan 11 '23 at 16:11
  • Also, see my last edit. It seems like you want to `defer` the call to `function2` until the `iif` is subscribed. – Tobias S. Jan 11 '23 at 16:14
  • Have edited my OP with the implementation - I have a `console.log` inside that function, but more importantly I'm running the .net API locally - when it gets called correctly my breakpoint should be hit. `defer()` doesn't seem to make a difference also ^ – Loz Jan 11 '23 at 16:21
  • I removed the `iif` and replaced it with a normal `if/else` for now. It should definitely call the function. – Tobias S. Jan 11 '23 at 16:29
  • 1
    Yep, that's sorted it! Weird that iif() was having trouble but it does what I need it to now, thanks a lot for taking the time to go through this with me, much appreciated :) – Loz Jan 11 '23 at 16:36