0

I have the following function that:

Take a value from my store => Transform it to csv => Create a download link

  downloadCSV() {
    let href;
    this.store$.select(MissionsStoreSelectors.selectedRoute).pipe(take(1)).subscribe((route) => {
      if (route) {
        const csv = this.csvManipulatorService.generateCSVFromJSON({filename: route.routeId, data: route.waypoints, columns : ['id', 'position', 'rotation']});
        href = this.domSanitizer.bypassSecurityTrustUrl('data:text/csv,' + encodeURIComponent(csv));
      }
    });
    return href;
  }

(don't take care about the fact there is no guard, is simplified)

I thought the subscribe was not synchronous. But from this accepted answer:

How to get current value of State object with @ngrx/store?

it looks like it is not.

My question is is this going to work 100% of the time? Is the subscribe with a take always synchronous ?

youri
  • 1,140
  • 8
  • 19
Bobby
  • 4,372
  • 8
  • 47
  • 103
  • All observables (anything you can subscribe) should be considered not synchronous. But you can always convert an observable to promise and change your function to an async function. That's a solid approach if you need only one value as fast as possible (null or defined) – Doğancan Arabacı Jun 21 '19 at 11:01
  • Side note: using the pipeable `select` operator is the preferred method since 6.1.0. – Corey Jun 21 '19 at 19:44

1 Answers1

2

Two answers

  1. No. Subscribe with take is not always synchronous. What makes an observable async/sync is the producer of the emitted values, not the operators in the stream. Now, if you did not use take(1) or unsubscribe from the Observable at some point, it's not a synchronicity issue you would have, but a memory leak. The take(1) is important, but not because of synchronicity concerns.

  2. In your case, you are using a function to create and subscribe to an observable that gets the latest data from the store, which is basically a BehaviorSubject in that it always returns the most recent values on subscribe, and then the updates after that. This is a synchronous action, and I see no reason why it wouldn't always work.

This is testable by creating a simple synchronous Observable, modifying a value, and then logging it out immediately.

https://stackblitz.com/edit/rxjs-ujs6u9

You could simplify this however and return the Observable itself instead of wrapping it in a function:

csvHref$ = this.store$.select(MissionsStoreSelectors.selectedRoute).pipe(
  take(1),
  map(route => {
    if (!route) {
      return null;
    }

    const csv = this.csvManipulatorService.generateCSVFromJSON({
      filename: route.routeId,
      data: route.waypoints,
      columns : ['id', 'position', 'rotation']
    });

    return this.domSanitizer.bypassSecurityTrustUrl(`data:text/csv,${encodeURIComponent(csv)}`);
  })
)

And then instead of calling it like a function, you just subscribe to it to get the value.

csvHref$.subscribe(href => doSomethingWithHref(href));

Based on your usage that may not be possible, but you should think of Observables as being functions already.

I recommend https://medium.com/@benlesh/learning-observable-by-building-observable-d5da57405d87 as a good read.

Corey
  • 463
  • 3
  • 12
  • 1
    Your second point doesn't sound right. An observable being a Subject doesn't make subscribing and getting the first value a synchronous operation. That's not even true for ReplaySubject. It is true for BehaviorSubject, though. – Ingo Bürk Jun 21 '19 at 17:08
  • 1
    Just another point: BehaviorSubject has an actually synchronous getValue method. If you want a sync value, you should just use that. If the library exposes it as an Observable rather than a BehaviorSubject, you'd be relying on an implementation detail anyway (which you shouldn't) rather than the contract. – Ingo Bürk Jun 21 '19 at 17:43
  • @IngoBürk your point is correct about BehaviorSubject. However, ngrx circa 2.0 removed their `value` implementation. Instead they guarantee that the subscribing to the store will always synchronously return the current state. – Corey Jun 21 '19 at 19:57
  • OK, if the library explicitly states so, then it's also "OK" to rely on it. Still feels somewhat dirty and I'd avoid it if at all possible, though. – Ingo Bürk Jun 21 '19 at 20:07
  • 1
    NgRx feels somewhat dirty. – Corey Jun 21 '19 at 20:11