luci-base: luci.js: auto-coalesce ubus requests
Extend LuCI.Request to automatically coalesce subsequent requests to ubus resources into single batch requests. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
90288b2e2c
commit
b21a2813b4
1 changed files with 98 additions and 7 deletions
|
@ -161,33 +161,121 @@
|
||||||
|
|
||||||
var Response = Class.extend({
|
var Response = Class.extend({
|
||||||
__name__: 'LuCI.XHR.Response',
|
__name__: 'LuCI.XHR.Response',
|
||||||
__init__: function(xhr, url, duration) {
|
__init__: function(xhr, url, duration, headers, content) {
|
||||||
this.ok = (xhr.status >= 200 && xhr.status <= 299);
|
this.ok = (xhr.status >= 200 && xhr.status <= 299);
|
||||||
this.status = xhr.status;
|
this.status = xhr.status;
|
||||||
this.statusText = xhr.statusText;
|
this.statusText = xhr.statusText;
|
||||||
this.responseText = xhr.responseText;
|
this.headers = (headers != null) ? headers : new Headers(xhr);
|
||||||
this.headers = new Headers(xhr);
|
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.xhr = xhr;
|
this.xhr = xhr;
|
||||||
|
|
||||||
|
if (content != null && typeof(content) == 'object') {
|
||||||
|
this.responseJSON = content;
|
||||||
|
this.responseText = null;
|
||||||
|
}
|
||||||
|
else if (content != null) {
|
||||||
|
this.responseJSON = null;
|
||||||
|
this.responseText = String(content);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.responseJSON = null;
|
||||||
|
this.responseText = xhr.responseText;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clone: function(content) {
|
||||||
|
var copy = new Response(this.xhr, this.url, this.duration, this.headers, content);
|
||||||
|
|
||||||
|
copy.ok = this.ok;
|
||||||
|
copy.status = this.status;
|
||||||
|
copy.statusText = this.statusText;
|
||||||
|
|
||||||
|
return copy;
|
||||||
},
|
},
|
||||||
|
|
||||||
json: function() {
|
json: function() {
|
||||||
return JSON.parse(this.responseText);
|
if (this.responseJSON == null)
|
||||||
|
this.responseJSON = JSON.parse(this.responseText);
|
||||||
|
|
||||||
|
return this.responseJSON;
|
||||||
},
|
},
|
||||||
|
|
||||||
text: function() {
|
text: function() {
|
||||||
|
if (this.responseText == null && this.responseJSON != null)
|
||||||
|
this.responseText = JSON.stringify(this.responseJSON);
|
||||||
|
|
||||||
return this.responseText;
|
return this.responseText;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var requestQueue = [],
|
||||||
|
rpcBaseURL = null;
|
||||||
|
|
||||||
|
function isQueueableRequest(opt) {
|
||||||
|
if (!classes.rpc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (opt.method != 'POST' || typeof(opt.content) != 'object')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (opt.nobatch === true)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rpcBaseURL == null)
|
||||||
|
rpcBaseURL = Request.expandURL(classes.rpc.getBaseURL());
|
||||||
|
|
||||||
|
return (rpcBaseURL != null && opt.url.indexOf(rpcBaseURL) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushRequestQueue() {
|
||||||
|
if (!requestQueue.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var reqopt = Object.assign({}, requestQueue[0][0], { content: [], nobatch: true }),
|
||||||
|
batch = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < requestQueue.length; i++) {
|
||||||
|
batch[i] = requestQueue[i];
|
||||||
|
reqopt.content[i] = batch[i][0].content;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestQueue.length = 0;
|
||||||
|
|
||||||
|
Request.request(rpcBaseURL, reqopt).then(function(reply) {
|
||||||
|
var json = null, req = null;
|
||||||
|
|
||||||
|
try { json = reply.json() }
|
||||||
|
catch(e) { }
|
||||||
|
|
||||||
|
while ((req = batch.shift()) != null)
|
||||||
|
if (Array.isArray(json) && json.length)
|
||||||
|
req[2].call(reqopt, reply.clone(json.shift()));
|
||||||
|
else
|
||||||
|
req[1].call(reqopt, new Error('No related RPC reply'));
|
||||||
|
}).catch(function(error) {
|
||||||
|
var req = null;
|
||||||
|
|
||||||
|
while ((req = batch.shift()) != null)
|
||||||
|
req[1].call(reqopt, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var Request = Class.singleton({
|
var Request = Class.singleton({
|
||||||
__name__: 'LuCI.Request',
|
__name__: 'LuCI.Request',
|
||||||
|
|
||||||
interceptors: [],
|
interceptors: [],
|
||||||
|
|
||||||
|
expandURL: function(url) {
|
||||||
|
if (!/^(?:[^/]+:)?\/\//.test(url))
|
||||||
|
url = location.protocol + '//' + location.host + url;
|
||||||
|
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
request: function(target, options) {
|
request: function(target, options) {
|
||||||
var state = { xhr: new XMLHttpRequest(), url: target, start: Date.now() },
|
var state = { xhr: new XMLHttpRequest(), url: this.expandURL(target), start: Date.now() },
|
||||||
opt = Object.assign({}, options, state),
|
opt = Object.assign({}, options, state),
|
||||||
content = null,
|
content = null,
|
||||||
contenttype = null,
|
contenttype = null,
|
||||||
|
@ -231,8 +319,11 @@
|
||||||
if (!opt.cache)
|
if (!opt.cache)
|
||||||
opt.url += ((/\?/).test(opt.url) ? '&' : '?') + (new Date()).getTime();
|
opt.url += ((/\?/).test(opt.url) ? '&' : '?') + (new Date()).getTime();
|
||||||
|
|
||||||
if (!/^(?:[^/]+:)?\/\//.test(opt.url))
|
if (isQueueableRequest(opt)) {
|
||||||
opt.url = location.protocol + '//' + location.host + opt.url;
|
requestQueue.push([opt, rejectFn, resolveFn]);
|
||||||
|
requestAnimationFrame(flushRequestQueue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ('username' in opt && 'password' in opt)
|
if ('username' in opt && 'password' in opt)
|
||||||
opt.xhr.open(opt.method, opt.url, true, opt.username, opt.password);
|
opt.xhr.open(opt.method, opt.url, true, opt.username, opt.password);
|
||||||
|
|
Loading…
Reference in a new issue