1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 17:02:41 +01:00
phorge-phorge/webroot/rsrc/externals/javelin/lib/Router.js
epriestley 2b4c551b0e Provide a global router for Ajax requests
Summary:
Fixes T430. Fixes T4834. Obsoletes D7641. Currently, we do some things less-well than we could:

  - We just let the browser queue and prioritize requests, so if you load a revision with 50 changes and then click "Award Token", the action blocks until the changes load in most/all browsers. It would be better to prioritize this action and queue it immediately.
  - Similarly, changes tend to load in order, even if the user has clicked to a specific file. When the user expresses a preference for a specific file, we should prioritize it.
  - We show a spinning GIF when waiting on requests. This is appropriate for some types of reuqests, but distracting for others.

To fix this:

  - Queue all (or, at least, most) requests into a new queue in JX.Router.
  - JX.Router handles prioritizing the requests. Principally:
    - You can submit a request with a specific priority (500 = general content loading, 1000 = default, 2000 = explicit user action) and JX.Router will get the higher stuff fired off sooner.
    - You can name requests and then adjust their prorities later, if the user expresses an interest in specific results.
  - Only use the spinner gif for "workflow" requests, which is bascially when the user clicked something and we're waiting on the server. I think it's useful and not-annoying in this case.
  - Don't show any status for draft requests.
  - For content requests, show a subtle hipster-style top loading bar.

Test Plan:
  - Viewed a diff with 93 changes, and clicked award token.
    - Prior to this patch, the action took many many seconds to resolve.
    - After this patch, it resolves quickly.
  - Viewed a diff with 93 changes and saw a pleasant subtle hipster-style loading bar.
  - Viewed a diff with 93 changes and typed some draft text. Previews populated fairly quickly and there was no spinner.
  - Viewed a diff with 93 changes and clicked something with workflow, saw a spinner after a moment.
  - Viewed a diff with 93 changes and clicked a file in the table of contents near the end of the list.
    - Prior to this patch, it took a long time to show up.
    - After this patch, it loads directly.

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T430, T4834

Differential Revision: https://secure.phabricator.com/D8979
2014-05-05 10:57:42 -07:00

115 lines
2.3 KiB
JavaScript

/**
* @provides javelin-router
* @requires javelin-install
* javelin-util
* @javelin
*/
/**
* Route requests. Primarily, this class provides a quality-of-service
* priority queue so large numbers of background loading tasks don't block
* interactive requests.
*/
JX.install('Router', {
construct: function() {
this._queue = [];
},
events: ['queue', 'start', 'done'],
members: {
_queue: null,
_active: 0,
_limit: 5,
queue: function(routable) {
this._queue.push(routable);
this.invoke('queue', routable);
this._update();
},
getRoutableByKey: function(key) {
for (var ii = 0; ii < this._queue.length; ii++) {
if (this._queue[ii].getKey() == key) {
return this._queue[ii];
}
}
return null;
},
/**
* Start new requests if we have slots free for them.
*/
_update: function() {
var active = this._active;
var limit = this._limit;
if (active >= limit) {
// If we're already at the request limit, we can't add any more
// requests.
return;
}
// If we only have one free slot, we reserve it for a request with
// at least priority 1000.
var minimum;
if ((active + 1) == limit) {
minimum = 1000;
} else {
minimum = 0;
}
var idx = this._getNextRoutable(minimum);
if (idx === null) {
return;
}
var routable = this._queue[idx];
this._queue.splice(idx, 1);
routable.listen('done', JX.bind(this, this._done, routable));
this._active++;
routable.start();
this.invoke('start', routable);
this._update();
},
_done: function(routable) {
this._active--;
this.invoke('done', routable);
this._update();
},
_getNextRoutable: function(minimum) {
var best = (minimum - 1);
var routable = null;
for (var ii = 0; ii < this._queue.length; ii++) {
var priority = this._queue[ii].getPriority();
if (priority > best) {
best = priority;
routable = ii;
}
}
return routable;
}
},
statics: {
_instance: null,
getInstance: function() {
if (!JX.Router._instance) {
JX.Router._instance = new JX.Router();
}
return JX.Router._instance;
}
}
});