arvgta / ajaxify

Ajaxify - The Ajax Plugin
https://4nf.org/
274 stars 124 forks source link

"100ms phenomenon" bug #179

Closed edito closed 4 years ago

edito commented 4 years ago

Better description of "100ms phenomenon" or to say multiple ajax asynchronous request handling issue

There is a bug that under specific circumstances occurs with ajaxify and triggers page refresh (ajaxify have built-in feature that, in case something goes wrong, forces page refresh as escape method, but there are some situations where error is false positive).

As there are two different types of requests and their mechanics that ajaxify handle, in further text I'll call them standard request (when user clicks on link) and prefetch request (when user hovers over link with [possible] intention for clicking on it).

There are several ways of reproducing this unwanted behavior and I'll start from the easiest one by adding some of ajaxify options that needs to be passed.

Option 1:

[settings]  prefetchoff: true,  memoryoff: true,  refresh: true

User has to do two standard requests in very short interval. 

(Situation 1) Double click any link and page will load but after that page refresh happens. 

Option 2: 

[settings] prefetchoff: false,  memoryoff: true

User has to do standard request and prefetch request immediately after it.

(Situation 1)  hover and click on link under 100ms after hovering, that is the time delay for prefetch to start. Two ajax requests are started, standard and prefetch one, the last one is changing variables before first one succeeds.

(Situation 2) click on one link and fast hover over other link to initiate prefetch, the rest is same as for situation 1. I mentioned this one because it can't be covered with "stop prefetch if user already clicked link" patch.

Deeper analysis and fix ideas

For all ways and situations there is a common thing, one request change shared variables (cb, plus, maybe even some others from setters/getters) for previous request that is not finished(succeeded) yet. cb and plus variables are especially important because they are part of ajax success handler, but changing those getters and setters could probably be issue, I didn't researched possibility, for example, that previous request on (asynchronous) success tries to cache response when newly started request already set variables for other URL. Just realized while describing this and mentioning cb variable, I actually saw situations inside console (logging turned on) where standard request is started, then prefetch kicks in and after processing both requests, only callback that is returned is the one for the prefetch (twice). Here is commented image of that kind of log.

After researching possibilities with ajax, my ideas are:

Standard requests are those which should be fast and reliable, so on users new request, every previous standard request should get aborted (xhr.abort(), also triggers ajax error but with status=abort, so it can be recognized and handled under current ajaxify error handler). This will also improve speed in situations where, for example, user request page with 20 images that are 300-400kb each, after that he changes mind when 5th image is loaded and requests "About" page that has few kilobytes of data. Without aborting first request all 20 images will get loaded together with "About" page data.

Just to mention that xhr have state tracking of request (xhr.readyState) well documented that empowers xhr.abort().

However, same abort option is not applicable for prefetch request in current ajax handler, because we don't want to stop standard request to do prefetch, but also in Second way (situation 2), we need both (standard request and prefetch) to be done as soon as possible. 

I thought about two ideas for this, splitting standard requests and prefetch handling, then it would be possible to do xhr.abort() for all previous prefetch requests when starting new, without impact on standard requests. Second and more complicated idea will be making something like query for prefetch requests. This can be discussed further later. 

Every other idea is welcome and appreciated.

commented_log

arvgta commented 4 years ago

Solved - at the very latest in course of this release

With many thanks to Edin once more!