Extend Your Reactive Logic in RxJS using Observable-like Proxies that Delay or Drop Events

Rares Matei
InstructorRares Matei
Share this video with your friends

Social Share Links

Send Tweet
Published 5 years ago
Updated 4 years ago

Our app is working! But now our manager comes in and tells us that some tasks in our app are finishing very fast, so users are seeing a short glimpse of the spooner which makes the app look glitchy. Our new requirement is to wait at least 2 seconds before showing the spinner. So without introducing any complexity into our main observable, we will create a new intermediary stream that will be a proxy between the observable that immediately tells us when to show the spinner and the one that actual shows it. This new proxy will delay the events accordingly.

Instructor: [0:00] This button here triggers a quick task that's over in 300 milliseconds. If I click, the spinner appeared and quickly vanished. I'll do that again. Click. It appears and then vanishes. Not the best experience and it looks a bit glitchy.

[0:13] Our virtual manager tells us that I need to change the spinner so that instead of showing it immediately, the spinner only shows once it's been active for at least two seconds. What does this mean? Well, if you imagine a timeline of two seconds and you have a quick task that only takes 300 milliseconds, then we don't want to show it.

[0:32] But if we have a collection of very short tasks that continuously intersect each other over a period of two seconds, then we do want to show it in that case. If it's a case where we have even a small breakage between them, we don't want to show it, because we consider these two separate independent instances that were less than two seconds each.

[0:53] Truth is, we don't even have to think about those scenarios. We've broken down our problems into very small bits so that if we need to work at this level or this level or even this level, we don't even have to think about concepts down here such as task starting or ending.

[1:09] I'll create a new floor and the moment the spinner becomes active to waiting for two seconds before showing it but cancel if it becomes inactive again in the meantime. The only input to this, the only information that we need to solve the problem are these two. When does the loader become active and when does the loader become inactive?

[1:29] What's going to happen is now this will be the answer we need for this question, when does the spinner need to show? Let's go to our code. I'll move right below the layer where we declared these two and I'll add a new comment and I'll copy our breakdown of the requirement. First, let's rename these to be more indicative of what they do, spinnerDeactivated and spinnerActivated.

[1:56] For the new implementation of this, the moment the spinner becomes active, switch to waiting for two seconds before emitting. We don't want to let the timer fire after its two seconds are up if the spinner became inactive in the meantime. I'll take until the spinner's deactivated. Finally, I'll need to make sure that I'm using spinnerDeactivated in here as well.

[2:20] Let's test this. I'll press it once. No spinner, even after two seconds. I'll press this a few times and the spinner now shows and keeps showing because there have been enough of these overlapping tasks over a period of two seconds to warrant showing it. If I go back to the first tab and trigger a really slow task, we can see that it still works.

[2:44] In summary, breaking down our problems previously helped us easily find a spot for our new requirement. It had two clear inputs that were already answered by these questions and it had a very clear output to our top-level requirement. All of that translated perfectly into our code as well.

[3:03] Because we created well encapsulated building blocks, we could simply declare another well-defined building block and insert it like a proxy between these sources and our top-level consumer. Our proxy simply responds to events from this, delaying them as necessary, and fires them to the next block in the chain.

Colin Principe
Colin Principe
~ 4 years ago

In order to get this to work in my environment I had to change the import from rxjs/operators to rxjs and put spinnerDeactivated and flashThreshold into an array:

https://jsfiddle.net/Lcxgntwb/

Environment: OSX, Webstorm 2020.1.1, Firefox 75 64-bit.

Colin Principe
Colin Principe
~ 4 years ago

Sorry, by "this" I mean the spinner show time on the Almost Quick Task.

Rares Matei
Rares Mateiinstructor
~ 4 years ago

Hi Colin! I think you your comment is for the next lesson - 12.

And you are correct, we need to import "combineLatest" from the top-level "rxjs" package. If you look in the GitHub, that's how we did it: https://github.com/rarmatei/egghead-thinking-reactively/blob/lesson10/src/lesson-code/TaskProgressService.js

And combineLatest accepts a variable number of observables as input (not an array), so I'm surprised it's not working for you:

  • what version of rxjs are you using?
  • can you clone my repository, run npm install and let me know if it's still not working for you?
Markdown supported.
Become a member to join the discussionEnroll Today