Imagine I have such setup. Imagine I have some map on my page and next to it there is a dialog where there is a toggle button.
Imagine when user clicks the toggle button a different request (depending on toggle state) is sent to server (which returns different type of map data depending on the request). If toggle is clicked once it receives say data about restaurants, next time it is clicked it receives data about hospitals and so on.
Also imagine when user drags a map, a refresh signal is sent to server, which sends same type of data but with updated information. For example, if user drags map when hospitals are shown on the map, again hospitals are received but with updated location.
Problem
Imagine two situations:
Assume user quickly clicks the toggle several times - then following thing will happen. First, request is sent to receive the restaurant data. But restaurant data has not arrived yet - and now before the restaurant data is received, the next toggle click happens, and request is sent to get hospital data (normally this second click on the toggle would delete restaurant data but since it is not on the map yet, it can't). So in the end we will end up with both restaurant and hospital data on the map which is what we don't want.
Imagine user clicks toggle and request is sent to receive restaurant data. But before restaurant data is received imagine user drags map (which causes refresh). What happens now due to drag is that since currently there is hospital data on the map, due to refresh, request to get updated hospital data will be sent. In the end we will again end up with hospital and restaurant data on the map.
I hope you can see the pattern of the kind of problems I am encountering here.
What is the best practice to deal with such situations?
I have solutions in my mind. One has the impact on User Experience and UI, the other one involves sending the last request. I will explain how:
1. Disabling the map and the button until the request is complete.
So what you can do is a loading or overlay div that stays until the request info is returned. Until this, the user is not allowed to use the map or toggle. This is UI impact but I have seen sites behaving this manner.
2. Map position.
The other option is to store the map position in like local storage and match the map position again on request success. If the map is on a different position, serve a message like Search in this area etc. Google maps behave something like this manner.
3. Serve the latest ajax request and abort others:
This can be done, by pushing the requests in a queue/order and send the last one to get the response. like if you are catering it via ajax, so some code like below code, if I call getFoo for 10 times, the last one will be fired
var ajax = null;
var getFoo = function() {
if(ajax) ajax.abort();
ajax= $.ajax({});
};
What you're describing is a simple case of race conditions.
There is no general solution for this kind of problem, it needs to be handled case-by-case. But in most cases, you probably want to display the results of the latest action user has done. If you don't have another way of knowing which request is which, you can attach some sort of identifier to the request, for example a GUID, and only display the data when the corresponding request is done.
Sometimes, the solution can be simply to disable all actions which could cause a race condition for the duration of the request, but this can deteriorate the user experience. Take for example Google Docs online editor, imagine that the whole editor would get disabled every time the auto-save function is triggered. In cases like this, it would be beneficial to store every update and compose the state from these actions. One of JavaScript that does state management like this is Redux. If you want to store data like this, you could use a database, such as EventStore.
Related
If my client-side application displays a list of items requested from my API, and a user deletes an item, is it best practice to then again call the list GET at the end of the successful HTTP DELETE promise and update the view? Or should the application state simply be updated locally after the delete, without hitting the API again?
I understand optimistically updating your application, but, that is more for view update performance rather than reducing server calls.
You should make another GET request to update the list if the list data may change without user interaction, eg: server-side updates, or another session that can change the same list
With the performance aspect, let the user see the entry deleted ASAP, then very shortly after they will see the updated changes (if there are any), and it won't disturb them.
If there is a deletion error, refresh the updated list regardless
EDIT: I'd also suggest using websockets
I've been doing some research about infinite scrolling and came across what people call "Lazy Loading". Now I have done it on one of my website elements (chatbox), and now I have 2 ways to do it. but I can't decide which one is more efficient than the other. Here are my ways:
Let's say I have 1,000,000 rows of data from database which I need to fetch all
1st way:
Load content from database, truncate it on the server-side code(PHP) then only show the first 50.
Upon user scroll on the page, another request will be sent to fetch the results again and display the next 50 and so on and so forth..
2nd way:
Load content from database, render it in my HTML as hidden elements but only displaying the first 50, then upon user scroll, show 50 more hidden elements.
The 1st way is requesting from the server whenever the need to display more results arises.
The 2nd way just does 1 request from the server, then hides the result except for the first few that should be shown.
I have no problem doing either of the two.
Now the dilemma, is that the 1st way is sending more HTTP requests, and the 2nd way (though sending only 1 HTTP request) is fetching huge data in a single request, which can be slow.
Which method is "better", and if there are other options, please let me know.
I know this is old question but want to give my opinion:
1st way
Is always preferred specially with that amount of rows, it is much more efficient to only request a set number of rows and if user wants to see more e.g click in next page another request is made to the server to fetch the next page, the response time will be much better, it will also make it easier to the client to manipulate the list if there is other processing that needs to be done before is returned to the user.
You also need to make sure that you are applying the limits in your DB query otherwise you will be loading all the objects into memory which is not efficient.
2nd way
If you fetch 1,000,000 rows at once the user will have to wait until the response comes back which can result in a bad user experience also as the number of rows returned keeps growing the response time will keep increasing and you can hit a time-out eventually, also consider that you will be loading all those objects into memory in your server before is returned.
The only use case I see for this approach is if you have a list that doesn't grow over time or that you have a set number of items that doesn't affect response time.
In my Project Management app I want to solve the issue of multiple users viewing the same Task record and 1 or more of them updating some of the Tasks Data while the others are viewing it.
Here is a scenario...
User #1 views Task #1 and User #2 views Task #1
Now User #2 updates the Task #1 Description
User #1 is now viewing the Task record but his view shows a different out-dated Description since User #2 had just updated it! Even worse he may want to edit the description himself which would completely over-write User #2's Description update.
That is 1 example of the issue. Just add more users and the issue multiplies!
Ideally I would use something like Sockets or a service like Pusher.com to update the data for all users as soon as an update by any user is made. However this project is going to be on hundreds of servers and has limited capabilities as far as server requirements so sockets and even a service like Pusher are out of the question!
Another idea for a solution is based on what Twitter does. If you view a persons Twitter page and they make a new post while you have there page loaded. It will show a notification message DIV to tell you there are X number of new posts and give you a link to click to reload the stream of posts with the latest posts.
I believe a similar approach could work for my project. If a user makes an update to any Task data while other users are viewing that Task record. It would show a Notification message on the Task modal window telling the user that the Task data has been updated and that they should reload the Task.
To make this work I know there will need to be some AJAX request made at some interval.
That AJAX request would then need to compare the timestamp of the last update made on the Task record and compare it with the time the user viewing the Task record started viewing it or since they last reloaded the task.
I feel like my logic is missing a piece of the puzzle though? Is this all correct or am I missing something?
Can someone explain how I can do this or tell me if what I have in mind is right?
I know that in short I simply need to determine if the Task last modified Timestamp is AFTER the other user started viewing the Task. At some point though I feel the users time should be updated too?
UPDATE
I completely forgot that Stack Overflow does this exact task on questions and answers! When you view a page on SO and a Answer is updated it will show a notification message telling you to reload the answer and provide a link to reload it. That is what I want to do!
StackOverflow uses Web Sockets to do this but in my app which is a plugin to be used on many different server configurations, I cannot use Sockets. I am trying to achieve similar result with AJAX. Even if it's an AJAX request made every 30 seconds to get the task modified Time and compare it to another to determine if user should reload task data would work in my case
Your question is too broad, but you are basically describing a Pub/Sub.
Idea
Whenever a user enters your site, he gets a token to identify them.
If he accesses a task, he subscribes to that task, which means that any modification on it would be alerted to him.
He polls the server to check if there is any alert to him.
Implementation
Regarding implementation, you can set-up a list with each user's subscriptions.
Using your example:
User1 subscribes to (Task1, Task2)
User2 subscribes to (Task1)
With each subscription, you keep some value that represents the last state the user has about that topic (e.g, the last modification timestamp).
The user polls your application every n seconds. Whenever the request reaches your application, you check the user's subscriptions, and check if the timestamp has changed (if they have the latest). If so, you update the last state that the user has and retrieves him the new values for the tasks that changed.
Things to consider
This list of subscriptions will be constantly accessed, so becareful where you are going to store it. If in memory, consider that you will need to share it across the different instances (if you are load balancing). You can use Redis, or something like that.
You do not need to go to your database everytime you need to retrieve data. If someone is subscribing to it, keep it on a cache.
The concept and idea is fairly trivial, and the implementation shouldn't be any more difficult. You need a Last Updated timestamp for each task as you say, as well as a Last Update timestamp on the client if you go with the described approach. Generally, when a User is viewing a Task, you'd want to (on the client side, Javascript):
Query the server for the Last Updated timestamp of the Task being viewed with AJAX.
If the Last Updated timestamp of the Task being viewed is greater (newer) than the Last Update timestamp on the client goto step 5.
Wait (asynchronously) n seconds.
Goto step 1.
Notify User that the Task being viewed has been updated.
End (since it is already known to the client that the Task has been updated, there is no need to keep polling whether it's been updated or not).
One approach could be simply creating an asynchronous interval using setInterval() that would be cleared once it has been determined an update occurred, and the user is shown a message afterwards.
var lastUpdate = Date.now();
var intervalDuration = 30000; // 30 seconds
var interval = setInterval(function () {
var xhr = new XMLHttpRequest();
...
xhr.onload = function () {
if (...) { // if the Task's Last Updated timestamp is newer than lastUpdate
clearInterval(interval);
// show message to user that the Task has been updated
}
};
}, intervalDuration);
I can imagine in a significantly massive system this approach could be easily inadequate, but for non-enterprise grade solutions or for follow-up UI improvements this can be a quick, cheap, and spectacular solution.
Of course there are potentially more robust and flexible alternatives to this: long-polling or websockets.
Context
Single page / ajax web app
Basic code structure
LocationManager (responsible for updating the browser hash and switching the application location to a different tile)
Page/Tile Flow
Basic Info > Household Info > Vehicle Info > Purchase Options > Review Order > Enter Payment and Submit
Problem
When the user navigates from Purchase Options to Review Order, a long (5-8 second) service call is made to calculate order details. Upon the call's resolution, the callback is designed to navigate the user to Review Order page. The issue is, if the user clicks back during that time and goes back to Household Info, as soon as the call resolves, they will be "automatically" brought to Review Order. Very awkward user experience.
Limitations
Canceling the call is not an option. Need a solution to handle the navigation.
Current Proposed Implementation
Save "currentLocation" prior to making the calculateOrder call.
Pass the "currentLocation" in the callback to the setLocation method as intendedStartingPoint.
Inside setLocation method if(intendedStartingPoint === Locationmanager.currentLocation) {//Navigate}
To sum it up, if the user changes the location while the call is in progress, upon the call's resolution, we won't navigate since the user doesn't expect to be navigated to Review Order at that point.
This works, right?
The Catch
We have many places in the app where setLocation is called within a callback for a long-running call. This means that I will have to update all the setLocation calls with a new parameter - intendedStartingPoint. While it makes sense to me, it does seem like it has potential to get a bit cluttered.
Any ideas on how to clean it up and centralize it?
So, right now a user can click the Calculate button on a Purchase Options page. You then display some kind of a loading indicator (hopefully)
and send an asynchronous request to a server with setLocation('ReviewOrder') attached in a continuation. There is quite a number of places in the application where you use this pattern.
The problem of unexpected (from a user point of view) redirects is there because with this approach server data retrieval and UI navigation are coupled. A solution that comes to mind is to decouple them and remove setLocation calls
from all long-running request continuations. It can work the following way.
When the user clicks the Calculate button, you start an asynchronous request and at the same time immediately navigate to the Review Order page (this is important from a UX perspective since users now clearly understand that the Calculate button navigates to Review Order). On the Review Order page, display a loading indicator saying something like 'please wait, about 10 seconds remaining...' When a request completes, hide the loading indicator and show the data.
This way your users will have a consistent UX knowing that whenever they click a button in your application the same thing happens (they navigate to a view), and there are no surprising automagical redirects.
Given that you can't prevent the user from navigating among the tiles, notifying her about the calculation delay won't solve the whole problem. You can tell the user the estimated time to completion, you can display a progress bar, and you can take her immediately to the Review Order tile to wait for the results, but if she navigates away from the tile, you're left with your original problem.
If the user chose to navigate away after all of that information, she must have made a conscious decision to interrupt the proceedings. It would be bad UX to transport her back to Review Order. What now?
You propose, quite reasonably, that the callback function sent with calculateOrder should pass an intendedStartingPoint parameter to setLocation. You worry that this would require you to modify every call to setLocation to accommodate the new parameter. Never fear, JavaScript offers a neat way to solve this dilemma.
You can add a new parameter to setLocation without modifying the existing calls. This merely requires that intendedStartingPoint be the last argument in setLocation's argument list. Then your new version of setLocation can check the value of intendedStartingPoint to see if it's undefined.
If intendedStartingPoint is undefined, you know that setLocation is receiving one of the old calls, the ones that don't pass intendedStartingPoint. In these cases you ignore intendedStartingPoint and proceed as before. Otherwise, you compare intendedStartingPoint to the current location and proceed according to the result of the comparison.
An even better approach would be to make the new parameter not intendedStartingPoint, but an object called options that contains intendedStartingPoint as one of its attributes. This allows you to pass further optional values to setLocation if the need arises in the future.
The new behavior of setLocation is quite simple. Before setting a new location, you check whether intendedStartingPoint is equal to the current location. If it is, you don't have to do anything because the user is already where she's intended to be. But if the intendedStartingPoint is different from the current location, the user has navigated away, so you do something like this:
if (LocationManager.currentLocation !== options.intendedStartingPoint) {
// Tell the user that the calculation has finished.
// Ask her if she wants to return to Review Order now.
}
First thing, calculate order details via asynchronous call and show/simulate a progress bar to the end-user via javascript.
The second thing: do not enforce ReviewOrder tile opening in your service callback function. As the service completes it's calculation, your callback function checks the current tile, and if it is not ReviewOrder tile, then it stores the calculated information in the Session or Local Storage.
As user navigates ReviewOrder tile, compare order details which came from the user with the stored order details (via hashing function, for example).
If hashcodes of user order details and stored order details are the same, then show saved order information, otherwise call the service again.
Important note: to prevent order forging, consider the following way:
Upon calculating order details on the server, generate unique order id, that will be returned to the user. Then store the calculated order details along with this id in the server database. If user did not change order details, your script will post only this order id to the server as a sign, that order has been accepted. Then read your database and process the order by this id.
If order was not completed then employ a scheduled task, that cleans up your database from non-completed orders (for example - orders, calculated 24 hours ago, but still not completed).
First of all, if the user is able to go back and change any entered information on previous pages, it is a must to invalidate any pending service calls. If a service call based upon outdated information returns, it must be discarded.
This means, if(intendedStartingPoint === Locationmanager.currentLocation) {//Navigate} is not sufficient. You have to do something like if(intendedStartingPoint === Locationmanager.currentLocation && /* no information altered in the meantime*/) {//Navigate}.
Now for your design question: It's a little hard to construct this without any concrete code, but you could do the following:
Provide means to register and manage long-running calls in LocationManager
Long-running calls should then always be registered with the LocationManager
LocationManager should assure that at most one long-running call is active at a moment
If any location change occurs, all (or the one active) long-running call must be invalidated
Call-back of a long-running call should check if it not has been invalidated, and only navigate if this is the case. LocationManager could do this in a unified manner for all call-backs.
New long-running calls could replace/invalidate an already running call or be rejected, as you like.
I hope this makes sense in your concrete situation.
I'm using inline-edit to update text in the database with AJAX. This is basically the process, pretty usual stuff:
text is not editable
I click the text, it becomes editable
I type the new text
then click to send the updated text to the database
then return the text to non-editable format
My question is when should I update the interface with the new data? Should I update it immediately before the ajax call, or should I wait for the update response to return from the database?
My concern:
If I don't update the interface immediately and wait to get the response from the database, then I've lost the asynchronous benefit that comes with ajax.
But if I update it immediately, then if the database response has an error, I somehow have to track the change I already made, and reverse it, which is a lot more work.
So how is this sort of thing usually done?
I think it is completely reasonable to wait for the response and update as a result of a callback. Doing so does not detract from the async approach. It is still fully async because you are not blocking the entire page or reloading it completely.
Plenty of times in apps, especially in mobile ones where the bandwidth might be limited, I will see a spinner indicating that the field is submitting. This does not hold up any other part of the app. Even stackoverflow does this when I use the mobile view. Rely on the callbacks in order to stay async and still be synced to database return values.
AJAX calls are pretty quick, excepting network issues of course. Personally, I don't think you will lose the benefit of AJAX by waiting for a response from the database. That is, unless you plan on it being slow because of server-side processing, etc...
Now if you were to set the textfield to a non-editable state, the user might think that his change has been accepted and will be confused when the server returns an error and the value is reverted to its original state. I would leave the field editable until the server returns.
If you are using jQuery it's pretty simple, but if you are using your homebrew ajax call script you will have to add some mechanism to see if everything went good or bad.
$.ajax({
url: '/ajax/doSomething.php',
type: 'POST',
dataType: 'json',
data: {
'q': $("#editableText").val()
},
success: function(json) {
$("#editableText").html(json.value);
},
error: {
alert('something went wrong!');
}
})
So when your doSomething.php returns true or false, our ajax calls does something according to it.
Yes the ajax calls are pretty fast, but before changing the data displayed on the page I guess you must be sure that everything went OK, else the user might leave the page without knowing if they have done the editing or not.
The case that you have mentioned is an Optimistic UI update. In this case you are assuming (implictly) that the update will be performed on the server without any error.
The disadvantage to this approach would be with a following scenario
User clicks on non-editable text
Text becomes editable
User types in new text
User clicks send
The UI changes to the new text and makes it uneditable
User closes the browser window (or navigates away from the page) before the reply ever comes back (assuming that the change was performed)
Next time the user logs in (or comes back to the page) they are confused as to why the change did not apply!
However you also want to use the asynchronous nature of ajax and make sure that the user can still interact with your app (or the rest of the page) as this change is being performed.
The way we do that (at my work-place) would typically be (using long polling or http push)
The user interacts with non-editable text
The text becomes editable
User types in new text
User clicks send
Instead of updating the text optimistically we show some kind of spinner (only on the text) that indicates to the user that we are waiting for some kind of response from the server. Note that since we are only disabling just the part of the page that shows this text the user does not have to wait for the ajax call to return in order to interact with the rest of the page. (In fact we have many cases that support updating multiple parts of the page in parallel). Think of gmail where you might be chatting with someone in the browser window and at the same time you get an email. The chat can go on and the email counter is also incremented. Both of them are on the same page but do not depend on each other (or wait for each other in any way)
When the update is complete we take away the spinner (which is usually shown using css - toggle class) and replace the element's value with the new text.
Lastly if you are doing something like this make sure that your server also has support for adding a pending class to the text field (that will cause a spinner to appear). This is done for the following reason
User updates text and submits
User immediately navigates to a different page
User then comes back to the original page again (and let us assume that the update is not yet complete)
Since your server knows that the field is being updated and can add a pending css class to the label / display field the page comes up loaded with a spinner.(On the other hand if there is no pending request there will be no spinner shown)
Finally when the long poller message comes back from the server the spinner is taken off and the current value is displayed