Suppose you have a web app that loads content via Ajax. Let’s bootstrap it:
When you press a
button, you trigger
onClickChangePage and dispatch a promise that loads (actually just delay a value but let’s not ruin the magic) a value and sets it as the new page.
All buttons have a different delay times.
Home is 1 second and
Posts 3 seconds.
I’m pretty sure you’ve seen this pattern in almost any framework or VanillaJS so what’s the problem? Just try for yourself:
Posts then quickly click on
Home will load soon then
Posts will overwrite it. If you think as your user you’d be upset since he where expecting the
Home page to be the one that persists on the screen beause it’s the last been clicked.
Alex had a pretty simple solution. Compare IDs whenever setting a new application state. Pretty much handles it. Let’s take a look:
The new lines are 2, 5 and 19. The code pretty much explains itself. The most recent click got recorded as current page and before rendering anything we compare identifiers.
Promise.race(iterable)method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise. (MDN)
Using Promise.race we can dispatch in parallel both the data loader promise and a promise that watches for changes. Basically, since the race resolves to the first promise that resolve and ignore the others, our render will not be called if a user change pages. Let’s code:
Now we have a pretty special promise called
pageChanged. It rejects as soon as it checks that the expected page is not the same. As you can track, the
currentPage variable increments every time a button click is triggered. That identifies a new page.
Even though my implementation of
pageChanged might not be the best,
Promise.race will fail fast. But unless there’s a magic framework (over the hundreds of thousands in JS ecosystem) that fits this logic, this might not be the best.
switcher function returns a switched on object with methods to change it’s state and the
wire method, that takes a value and returns it unless the switch is
Combining a fresh switch to your SPA router watching for page changes, you can easily turn off the switch (line 35). Whenever the page is changed (line 44), all
onPageChange listener functions gets triggered and old switches off. With a simple helper function (
pageUnchanged) you can chain a switch
wire method into a promise resolve and throw if the value that came wasn’t wanted anymore.
And you? What you think about these solutions for the same problem? Leave a comment.