Javascript ajax请求期间未触发websocket onmessage
我有一个执行Jquery.post数据库查询请求的web应用程序。我还有三个不同的web套接字连接,用于将状态更新从服务器推送到客户端(实时图表中的CPU和内存统计、数据库状态和查询队列)。当查询没有运行时,一切都会顺利进行,但是一旦查询启动(post请求),那么三个web套接字连接似乎会在等待查询返回时挂起/阻塞。我读过这篇文章,但没有找到任何相关的答案……我怀疑这可能是我自己的愚蠢行为……但这让我在一天中的大部分时间里都在挠头。我想我可能会尝试将web套接字连接移动到web workers…但从理论上讲,帖子一开始就不应该被阻止…因此,这里是相关的代码片段…完整的源代码是几千行代码…因此我不想让任何人被它淹没…但如果它有用,我可以展示它。所以,最大的问题是我做错了什么?或者,我是否误解了AJAX调用在阻塞方面的工作方式Javascript ajax请求期间未触发websocket onmessage,javascript,perl,mojolicious,Javascript,Perl,Mojolicious,我有一个执行Jquery.post数据库查询请求的web应用程序。我还有三个不同的web套接字连接,用于将状态更新从服务器推送到客户端(实时图表中的CPU和内存统计、数据库状态和查询队列)。当查询没有运行时,一切都会顺利进行,但是一旦查询启动(post请求),那么三个web套接字连接似乎会在等待查询返回时挂起/阻塞。我读过这篇文章,但没有找到任何相关的答案……我怀疑这可能是我自己的愚蠢行为……但这让我在一天中的大部分时间里都在挠头。我想我可能会尝试将web套接字连接移动到web workers…
// query execution button that grabs the query for the most recently focused query source (SPARQL editor, history, or canned)
$("#querySubmitButton").on("click", function(e) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Let's make sure we are clearing out the work area and the popup contents
$("#viz").empty();
// Get YASQE to tell us what type of query we are running
var queryType = editor.getQueryType();
// refactored so that we can clean up the on-click function and also make other query types in a more modular way
switch(queryType) {
case 'SELECT':
sparqlSelect();
break;
case 'CONSTRUCT':
sparqlConstruct();
break;
case 'ASK':
sparqlAsk();
break;
case 'DESCRIBE':
sparqlDescribe();
break;
case 'INSERT':
sparqlInsert();
break;
default:
popup.show("Unrecognized query type.","error");
break;
}
});
// Functions to do each of the query types (SELECT, CONSTRUCT, ASK, DESCRIBE, INSERT)
// SELECT
function sparqlSelect() {
$.post("sparqlSelect", { database: $("#DB_label").html(),'query': editor.getValue() }).done(function(data, textStatus, xhr) {
// Enable the query button
$("#querySubmitButton").removeAttr('disabled');
// If the query worked, store it
storeQueryHistory(query);
// if the previous query was a CONSTRUCT, then lets hide the graph metrics button
$("#nav-trigger-graphStatistics").fadeOut(800);
// Need to slide the query menu back
sliders("in",$("#nav-trigger-query").attr("id"));
var columns = [];
var fields = [];
var comboboxFields = [];
// Hide the graph search panel
$("#graphSearch").fadeOut(1400);
// Show the results and visualization button/tab
$("#nav-trigger-results").fadeIn(1400);
$("#nav-trigger-visualization").fadeIn(1400);
$.each(data.results.head.vars, function(index, value) {
columns.push({'field': value, 'title': value});
var to = {};
to[value] = {type: "string"};
fields.push(to);
// Let's also populate the two Comboboxes for the Visualization while we are at it
comboboxFields.push({'text': value, 'value': value});
});
// Now, set the two combobox datasources for visualizations
var categoriesDS = new kendo.data.DataSource({
data: comboboxFields
});
vizCategoryAxis.setDataSource(categoriesDS);
var valuesDS = new kendo.data.DataSource({
data: comboboxFields
});
vizValueAxis.setDataSource(valuesDS);
var dataBindings = [];
$.each(data.results.results.bindings, function(index1, value) {
var tempobj = {};
$.each(value, function(k1,v1) {
tempobj[k1] = v1.value;
});
tempobj.id=index1;
dataBindings.push(tempobj);
});
var configuration = {
dataSource: {
data: dataBindings,
pageSize: 25
},
height: 400,
scrollable: true,
sortable: true,
filterable: true,
reorderable: true,
resizable: true,
toolbar: ["excel"],
excel: {
allPages: true,
filterable: true,
proxyURL: "/saveExcel"
},
pageable: {
input: true,
numeric: false,
pageSizes: true
},
'columns': columns,
dataBound: function(e) {
$(e.sender.element).find('td').each(function() {
var temp = $(this).html();
if (isUrl(temp)) {
$(this).html('<a href="' + temp + '" target="_blank">' + temp + '</a>');
}
});
}
};
// Create the popup window
var gridWindow = $("#resultsPopup").kendoWindow({
width: "70%",
title: "Query Results",
actions: [
"Minimize",
"Maximize",
"Close"
]
}).data('kendoWindow');
// Center and show the popup window
gridWindow.center().open();
// Create/update/refresh the grid
resultsGrid.setOptions(configuration);
resultsGrid.dataSource.page(1);
$("#nav-trigger-results").on('click',function() {
// Center and show the popup window
gridWindow.center().open();
});
}).fail(function(xhr) {
// If we are timed-out
if (xhr.status === 401) {
// First, clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
// Next, disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
popup.show("Session for " + host + " has timed out, please log back in.","error");
}
else {
// Enable the query button
$("#querySubmitButton").removeAttr('disabled');
popup.show("Error, no results (" + xhr.status + " " + xhr.statusText + ")","error");
}
});
}
// Function to connect to the query queue websocket
function queueWebsocketConnect() {
var qws = new WebSocket('wss://endeavour:3000/queue');
// Let's disconnect our Websocket connections when we leave the app
$(window).on('unload', function() {
console.log('Websocket connection closed');
qws.close();
});
// Status websocket onopen
qws.onopen = function () {
console.log('Websocket connection opened');
popup.show("Websocket connection opened","success");
};
qws.onclose = function (event) {
console.log('Websocket connection closed');
popup.show("Websocket connection closed","info");
};
qws.onmessage = function (msg) {
var res = JSON.parse(msg.data);
var tableRows = '<thead><tr><td>Query Position</td><td>Query ID</td><td>Kill/Cancel Query</td></tr></thead><tbody>';
if (res.executing != null && res.entry.length > 0) {
$("#queryQueue").empty();
tableRows += '<tr><td>1</td><td>' + res.executing.id + '</td><td><input type="button" class="k-button" value="Kill"></td></tr>';
$.each(res.entry, function(index,object) {
tableRows += '<tr><td>' + (object.pos + 1) + '</td><td>' + object.query.id + '</td><td><input type="button" class="k-button" value="Cancel"></td></tr>';
});
tableRows += '</tbody>';
$("#queryQueue").html(tableRows);
}
else if (res.executing != null) {
$("#queryQueue").empty();
tableRows += '<tr><td>1</td><td>' + res.executing.id + '</td><td><input type="button" class="k-button" value="Kill"></td></tr>';
tableRows += '</tbody>';
$("#queryQueue").html(tableRows);
}
else {
console.log(res);
$("#queryQueue").empty();
}
};
}
// Function to connect to the stats websocket
function websocketConnect () {
// Set up websocket connection for system stats
var ws = new WebSocket('wss://endeavour:3000/stats');
// Let's disconnect our Websocket connections when we leave the app
$(window).on('unload', function() {
console.log('Websocket connection closed');
ws.close();
});
// Status websocket onopen
ws.onopen = function () {
console.log('Websocket connection opened');
popup.show("Websocket connection opened","success");
};
// Status websocket onclose
ws.onclose = function (event) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
// Clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
console.log('Websocket connection closed');
popup.show("Websocket connection closed","error");
$("#websocketReconnectButtonYes").on('click', function() {
websocketConnect();
queueWebsocketConnect();
websocketReconnect.close();
});
$("#websocketReconnectButtonNo").on('click', function() {
websocketReconnect.close();
});
websocketReconnect.center().open();
};
// When updates are received, push them out to update the details
var logoutCount = 0;
ws.onmessage = function (msg) {
if (msg.data === 'loggedOut') {
// Ensure we only emit this one time instead of a stream of them
if (logoutCount == 0) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
// Clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
console.log("Session for " + $("#host_label").html() + " has timed out, please log back in.");
popup.show("Session for " + $("#host_label").html() + " has timed out, please log back in.","error");
}
logoutCount = 1;
}
else {
logoutCount = 0;
var res = JSON.parse(msg.data);
var host = $("#host_label").html();
var pdatabase = $("#DB_label").html();
var pstatus = $("#status_label").html();
// Disable the query button unless the database is "CONNECTED"
if ($("#status_label").html() !== res.current.databaseStatus) {
if (res.current.databaseStatus !== "CONNECTED") {
$("#querySubmitButton").attr('disabled',true);
}
else {
$("#querySubmitButton").removeAttr('disabled');
}
if (res.current.databaseStatus == 'CONNECTED' || res.current.databaseStatus == 'STOPPED') {
$("#startDB").removeAttr('disabled');
}
else {
$("#startDB").attr('disabled',true);
}
}
// Maybe a more intelligent way to do this, but need to make sure that if the cookie is still valid, then populate the database login stuff
if ($("#dbConfigHost").val() == "" && $("#dbConfigUser").val() == "") {
$("#dbConfigHost").val(res.host);
$("#dbConfigUser").val(res.user);
// Change "login" tab text color to green so we know we are logged in
var styles = { 'color': "#C5E6CC" };
$("#nav-trigger-login").css(styles);
var databasesDS = new kendo.data.DataSource({
data: res.databases.database
});
databasePicker.setDataSource(databasesDS);
}
// Update the labels when values change
if (res.host != $("#host_label").html()) {
$("#host_label").html(res.host);
popup.show("Host changed to " + res.host,"info");
}
if (pdatabase != res.current.name) {
$("#DB_label").html(res.current.name);
popup.show("Database changed to " + res.current.name ,"info");
}
if (pstatus != res.current.databaseStatus) {
$("#status_label").html(res.current.databaseStatus);
}
// Update the sparklines
cpulog.options.series[0].data = res.system.cpu;
cpulog.refresh();
memlog.options.series[0].data = res.system.mem;
memlog.refresh();
}
};
// Open the websocket connection to listen for changes to the query list
var queryWS = new WebSocket('wss://endeavour:3000/queryList');
queryWS.onmessage = function(msg) {
var res = JSON.parse(msg.data);
var queriesDS = new kendo.data.DataSource({
data: res
});
cannedQuery.setDataSource(queriesDS);
};
}
//获取最近关注的查询源(SPARQL编辑器、历史记录或目录)的查询的查询执行按钮
$(“#查询SubmitButton”)。在(“单击”上,函数(e){
//禁用查询按钮
$(“#querySubmitButton”).attr('disabled',true);
//让我们确保清除工作区和弹出内容
$(“#viz”).empty();
//让YASQE告诉我们正在运行什么类型的查询
var queryType=editor.getQueryType();
//进行了重构,以便我们可以清理on-click函数,并以更模块化的方式创建其他查询类型
开关(查询类型){
案例“选择”:
sparqlSelect();
打破
案例“构造”:
sparqlConstruct();
打破
案例“询问”:
sparqlAsk();
打破
案例“描述”:
sparqlldescripe();
打破
案例“插入”:
sparqlsert();
打破
违约:
show(“无法识别的查询类型”,“错误”);
打破
}
});
//执行每种查询类型(选择、构造、询问、描述、插入)的函数
//挑选
函数sparqlSelect(){
$.post(“sparqlSelect”,{database:$(“#DB_label”).html(),'query':editor.getValue()}).done(函数(数据、文本状态、xhr){
//启用查询按钮
$(“#querySubmitButton”).removeAttr('disabled');
//如果查询有效,则存储它
storeQueryHistory(查询);
//如果前面的查询是一个构造,那么让我们隐藏graph metrics按钮
美元(“#导航触发图统计”)。淡出(800);
//需要向后滑动查询菜单
滑块(“in”,$(“导航触发器查询”).attr(“id”);
var列=[];
var字段=[];
var comboboxFields=[];
//隐藏图形搜索面板
美元(“#图形搜索”)。淡出(1400);
//显示结果和可视化按钮/选项卡
美元(“#导航触发结果”)。fadeIn(1400);
美元(“#导航触发可视化”)。fadeIn(1400);
$.each(data.results.head.vars,函数(索引,值){
push({'field':value,'title':value});
var to={};
to[value]={type:“string”};
字段。推(到);
//在进行可视化时,让我们也填充两个组合框
push({'text':value,'value':value});
});
//现在,为可视化设置两个combobox数据源
var categoriesDS=new kendo.data.DataSource({
数据:comboboxFields
});
vizCategoryAxis.setDataSource(categoriesDS);
var valuesDS=新的kendo.data.DataSource({
数据:comboboxFields
});
vizValueAxis.setDataSource(valuesDS);
var数据绑定=[];
$.each(data.results.results.bindings,函数(index1,value){
var tempobj={};
$.each(值、函数(k1、v1){
tempobj[k1]=v1.0值;
});
tempobj.id=index1;
数据绑定推送(tempobj);
});
变量配置={
数据源:{
数据:数据绑定,
页面大小:25
},
身高:400,
可滚动:对,
可排序:是的,
可过滤:正确,
可重定额:对,
可调整大小:正确,
工具栏:[“excel”],
卓越:{
是的,
可过滤:正确,
proxyURL:“/saveExcel”
},
可分页:{
输入:正确,
数字:false,
页面大小:正确
},
“列”:列,
数据绑定:函数(e){
$(e.sender.element).find('td').each(function(){
var temp=$(this.html();
if(isUrl(temp)){
$(this.html(“”);
}
});
}
};
//创建弹出窗口
var gridWindow=$(“#结果弹出”).kendoWindow({
宽度:“70%”,
标题:“查询结果”,
行动:[
“最小化”,
“最大化”,
“结束”
]
}).数据(“kendoWindow”);
//居中并显示弹出窗口
gridWindow.center().open();
//创建/更新/刷新网格
resultsGrid.setOptions(配置);
resultsGrid.dataSource.page(1);
$(“#导航触发结果”)。在('单击',函数()上{
//居中并显示弹出窗口
gridWindow.center().open();
});
}).失败(功能(xhr){
//如果我们超时了
如果(xhr.status==401){
//首先,清除主机、数据库和状态文本
$(“#主机标签”).html(“”);
$(“#DB_标签”).html(“”);
$(“#状态标签”).html(“”);
//接下来,禁用查询按钮
$(“#querySubmi
my $tx = $ua->get('http://foo.bar?query=getSomeFoo');
if($tx->success) {
$self->render($tx->res->content);
}
else {
$self->rendered($tx->res->code);
}
$ua->get('http://foo.bar?query=getSomeFoo' => sub {
my ($ua,$tx) = @_;
if($tx->success) {
$self->render($tx->res->content);
}
else {
$self->rendered($tx->res->code);
}
});