1

I recently dived into subscriptions of Subject/BehaviorSubject/ etc and I am looking for the goto approach when used in combinations with Promises.

Given is the example code below:

firebase.user.subscribe((user: any | null) => {
    fs.readFile('path/to/file')
         .then((buf: Buffer) => {
             this.modifySomeData = buf;
         });
});

I subscribe to a Subject that triggers whenever the user logs in or out of their service. Whenever this happens, I read a file from disk. This readFile event could potentially take longer than the next "login/logout" event. Of course, I am in JS and in an asynchronous environment. This means, my user code is not multithreaded, but still, the 2nd user event and 2nd readFile could theoretically be faster than the first readFile.

  1. First user event fired
  2. First readFile is executed
  3. Second user event is fired
  4. Second readFile is executed
  5. Second readFile is resolved <---
  6. First readFile is resolved <---

The order is mixed up. The silliest approach I could think of is to create a uuid before reading the file and check inside the promise if this is still the same. If it's not I discard the data.

Is there a better solution?

msanford
  • 11,803
  • 11
  • 66
  • 93
HelloWorld
  • 2,392
  • 3
  • 31
  • 68

2 Answers2

1

The first approach is to see if your event generation logic could handle waiting for event handling. For example, you can use a promise to wait for the event OR generate another event, say doneReadFile and only then send the next event. Usually, this is not the case for a generic (distributed) environment.

If event generation does not care about how long it took to handle events, you can still use the above approach but check for the intermediate event doneReadFile in the next event handler (login/logout). This can be achieved by implementing some kind of polling or busy-wait/sleep

stWrong
  • 334
  • 2
  • 14
1

If i have a process where older requests can be discarded i often keep a variable in scope to track the latest request and compare, similar to your UUID idea:

let lastRead: Promise<Buffer> | null = null;

firebase.user.subscribe((user: any | null) => {
    const read = lastRead = fs.readFile('path/to/file');
    read.then((buf: Buffer) => {
        if (read != lastRead)
            return;

        this.modifySomeData = buf;
    });
});

In this specific case, readFile also supports an abort signal. So you might also be able to abort the last request instead; you will still need to track it though.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • 1
    Thanks! I accepted it as an answer as it goes very much into the direction I had with the `uuid`, except you use a different expression. Thanks for confirming! – HelloWorld Aug 20 '21 at 19:35