我有一个jQuery文件,它也使用unserscore.js。它控制日期和不同地点的选择。对于其中一个页面,它还控制根据地点类型显示哪些视觉效果。我可以成功地使用 AJAX ,获得页面类型,但是我一直无法将该值传递给脚本中的公共变量。它是基于数据来自哪个WiFi点的。如果数据来自本地地点,页面应该显示一个d3气泡图。如果数据来自远程地点,页面应该显示一个地点Map。目前,我的功能是基于地点ID的硬编码,这是非常不理想的。为了根据地点使用的地点做出决定,我创建了一个 AJAX 调用来获取“地点”。使用console.log,我可以看到我从 AJAX 调用中得到了正确的结果,但是在将信息传递给变量以便使用它方面,我遗漏了一些东西。
下面是完整的jQuery文件:
define([
"ui/selects",
], function (SelectsUiClass) {
var global = this;
var MainControlsClass = function () {
// Private vars
var _this = this,
_xhr = null,
_selects = new SelectsUiClass(),
_dateRangeSelect,
_venueSelect,
_floorSelect,
_zoneSelect;
// Public vars
this.Selects = null;
this.spotName = null;
// Private Methods
var _construct = function () {
_dateRangeSelect = _selects.InitSelect('#mainControls-dateRange', _onSelectChange);
_venueSelect = _selects.InitSelect('#mainControls-venue', _onSelectChange);
_floorSelect = _selects.InitSelect('#mainControls-floor', _onSelectChange);
_zoneSelect = _selects.InitSelect('#mainControls-zone', _onSelectChange);
var value = _this.GetVenue();
_getChartDisplayDiv(value);
};
var _getChartDisplayDiv = function (venueId) {
var path = window.location.pathname,
pathArray = path.split("/"),
page = pathArray[pathArray.length - 1];
console.log('controlsjs 36, navigation page: ' , page);
console.log('controlsjs 37, venue value: ' , venueId);
_this.Load(venueId);
console.log('Controls 40, sPot Name = ', _this.spotName);
if (page === 'heatmap') {
if (venueId === 8 || venueId === 354) {
//make the bubble div visible
$("#heatmap-bubble").show();
//make the map div invisible
$("#heatmap-map").hide();
} else {
//make the map div visible
$("#heatmap-map").show();
//make the bubble div invisible
$("#heatmap-bubble").hide();
}
}
}
this.Load = function (venueId) {
console.log("Controls 66, Venue Id sent = ", venueId);
if (_xhr) {
_xhr.abort();
_xhr = null;
}
_this.SetLoading(true);
_xhr = $.ajax({
url: $("meta[name='root']").attr("content") + '/app/heatmap/spot',
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: {
venue_id: venueId
},
dataType: 'JSON',
async: true,
cache: false,
error: function (jqXHR, textStatus, errorThrown) {
_this.SetLoading(false);
},
success: function (response) {
_this.SetLoading(false);
console.log("Controls 90, Response of ajax call = ", response);
_this.Update(response);
}
});
};
// Public functions
this.SetLoading = function (option) {
if (_.isUndefined(option)) { option = false; }
if (this.spotName) { this.spotName.SetLoading(option); }
};
this.Update = function (data) {
if (_.isUndefined(data) || _.isNull(data)) {
console.log('Controls 106: Spot Name: ', data)
this.spotName = data;
}
};
var _getVenueData = function (venueId) {
for (var i = 0; i < venuesData.length; i++) {
if (venuesData[i].id === venueId) {
if (!_.isUndefined(venuesData[i].spot_data)) {
return venuesData[i].spot_data;
}
}
}
};
var _onVenueChange = function () {
var value = _this.GetVenue();
if (_.isNull(value)) {
return;
}
_getChartDisplayDiv(value);
//_setSelectValue(_venueSelect, value);
var venueData = _getVenueData(value);
console.log('Venue data received: ', venueData);
if (!_.isUndefined(venueData) && !_.isUndefined(venueData.floors)) {
_selects.UpdateSelect(_floorSelect, venueData.floors);
_onFloorChange();
}
};
var _onFloorChange = function () {
var value = _this.GetFloor(),
zones = [];
if (_.isNull(value)) {
return;
}
//_setSelectValue(_floorSelect, value);
if (_.isNumber(value)) {
var venueData = _getVenueData(_this.GetVenue()),
floors = venueData.floors;
for (var i = 0; i < floors.length; i++) {
if (floors[i].id === value) {
zones = floors[i].zones;
}
}
}
_selects.UpdateSelect(_zoneSelect, zones);
};
var _onZoneChange = function () {
var value = _this.GetZone();
if (_.isNull(value)) {
return;
}
//_setSelectValue(_zoneSelect, value);
};
var _onSelectChange = function (e) {
var t = $(e.target),
id = t.attr('id');
if (_venueSelect && _venueSelect.attr('id') === id) {
_onVenueChange();
} else if (_floorSelect && _floorSelect.attr('id') === id) {
_onFloorChange();
} else if (_zoneSelect && _zoneSelect.attr('id') === id) {
_onZoneChange();
}
EventDispatcher.Dispatch('Main.Controls.Change', _this, {
caller: id
});
};
// Public Methods
this.GetDateRange = function () {
return _selects.GetSelectValue(_dateRangeSelect);
};
this.GetDateRangeKey = function () {
if (_dateRangeSelect) {
var selected = _dateRangeSelect.find('option:selected');
if (selected.length) {
return selected.attr("data-key") || "";
}
}
return "";
};
this.GetVenue = function () {
return _selects.GetSelectValue(_venueSelect);
};
this.SetVenue = function (value) {
_selects.SetSelectValue(_venueSelect, value);
}
this.GetFloor = function () {
return _selects.GetSelectValue(_floorSelect);
};
this.SetFloor = function (value) {
_selects.SetSelectValue(_floorSelect, value);
}
this.GetZone = function () {
return _selects.GetSelectValue(_zoneSelect);
};
this.SetZone = function (value) {
_selects.SetSelectValue(_zoneSelect, value);
}
this.GetData = function () {
return {
dateRange: {
date: this.GetDateRange(),
key: this.GetDateRangeKey()
},
venue: this.GetVenue(),
floor: this.GetFloor(),
zone: this.GetZone()
};
};
// Init
_construct();
};
return MainControlsClass;
});
用于确定要显示的视觉对象接近顶部的函数:_获取图表显示分区:
var _getChartDisplayDiv = function (venueId) {
var path = window.location.pathname,
pathArray = path.split("/"),
page = pathArray[pathArray.length - 1];
_this.Load(venueId);
console.log('Controls 40, sPot Name = ', _this.spotName);
if (page === 'heatmap') {
if (venueId === 8 || venueId === 354) {
//make the bubble div visible
$("#heatmap-bubble").show();
//make the map div invisible
$("#heatmap-map").hide();
} else {
//make the map div visible
$("#heatmap-map").show();
//make the bubble div invisible
$("#heatmap-bubble").hide();
}
}
}
当我能够将“spot”信息传递给它或它使用的变量时,它应该如下所示:
var _getChartDisplayDiv = function (venueId) {
var path = window.location.pathname,
pathArray = path.split("/"),
page = pathArray[pathArray.length - 1];
_this.Load(venueId);
console.log('Controls 40, sPot Name = ', _this.spotName);
if (page === 'heatmap') {
if (_this.spotName === 'local' ) {
//make the bubble div visible
$("#heatmap-bubble").show();
//make the map div invisible
$("#heatmap-map").hide();
} else {
//make the map div visible
$("#heatmap-map").show();
//make the bubble div invisible
$("#heatmap-bubble").hide();
}
}
}
型
我 AJAX 调用在这里:
this.Load = function (venueId) {
console.log("Controls 66, Venue Id sent = ", venueId);
if (_xhr) {
_xhr.abort();
_xhr = null;
}
_this.SetLoading(true);
_xhr = $.ajax({
url: $("meta[name='root']").attr("content") + '/app/heatmap/spot',
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: {
venue_id: venueId
},
dataType: 'JSON',
async: true,
cache: false,
error: function (jqXHR, textStatus, errorThrown) {
_this.SetLoading(false);
},
success: function (response) {
_this.SetLoading(false);
console.log("Controls 90, Response of ajax call = ", response);
_this.Update(response);
}
});
};
这成功地获得了正确的位置,但是我一直无法将它传递给我可以使用的变量。我想我混淆了私有变量和公共变量。我尝试使用'this.update'函数将设置传递给公共变量'this.spotName',但是结果为null。我还尝试简单地返回 AJAX 调用的结果,但是我得到了一个“不是函数”的错误。2我怎样才能使 AJAX 调用的结果对我的'_getChartDisplayDiv'函数可用呢?
2条答案
按热度按时间qacovj5a1#
您的问题是您试图在
_this.spotName
赋值之前读取它的值。让我们来看看发生的步骤。当你调用
_getChartDisplayDiv(value)
时,_getChartDisplayDiv
函数首先调用_this.Load(venueId)
,然后Load
用success
回调提交一个 AJAX 请求,如下所示:当响应到达时,
success
回调函数将被调用,然后success
回调函数将调用_this.Update
,_this.Update
将设置您要查找的变量。您用于此目的的语法是正确的。“当响应到达”碰巧是一个不可预测的事件在未来。它可能是在10毫秒之后,可能需要2秒钟,或者请求可能完全超时。即使10毫秒已经是一个永恒的时间相比,你的浏览器执行所有其他代码在你的脚本。你可以相当肯定的是,当
$.ajax
返回的时候,success
回调尚未运行。当您传递回调时(
success
)到函数($.ajax
),并且回调在函数返回之前没有运行,这被称为异步回调,简称“async”。当回调可能被异步调用时,对于函数来说保证它 * 总是 * 异步运行是很重要的,因为这种情况需要以与同步调用回调时完全不同的方式来处理(也就是在函数返回之前)。你可以阅读更多关于this blogpost的技术细节。这正是$.ajax
所保证的:它在返回之前 * 决不 * 调用success
(或error
)回调,* 即使 * 在响应将足够快到达的假设情况下。$.ajax
返回后,Load
函数立即返回,此时_getChartDisplayDiv
函数继续执行。几乎在此之后,您打算立即读取_this.spotName
。$.ajax
已经返回,因此您可能希望此时success
回调已经被调用。不幸的是,异步回调比这更顽固,不仅异步回调在传递给它的函数返回之前不会运行;它不会运行,直到 any 当前执行的函数返回。除了
$.ajax
之外,Load
需要返回,_getChartDisplayDiv
需要返回,任何调用_getChartDisplayDiv
的函数需要返回,等等。整个调用堆栈需要展开。只有在那时(当响应真正到达时,可能要过几毫秒)success
回调函数才会被调用,这个游戏规则在JavaScript中称为event loop。解决方案比您想象的要简单:您只需要颠倒控制的顺序。当您想要更新图表时,您可以在响应到达时更新图表,而不是试图强制数据离开请求。您也可以直接触发请求,而不是试图直接更新图表。具体到您的情况,您只需要进行三项更改:
1.在当前调用
_getChartDisplayDiv
的地方,改为调用_this.Load
。1.删除
_getChartDisplayDiv
函数中调用_this.Load
的行。1.在
success
处理程序的末尾,添加调用_getChartDisplayDiv
的行。顺便说一句,使用一个合适的应用程序框架将使管理这类事情变得容易得多,在您的情况下,我建议尝试Backbone;它构建在Underscore和jQuery之上,并且不受任何影响,因此您可以逐步采用它,而不必从根本上改变您的工作方式。
7y4bm7vi2#
我对
underscore.js
不熟悉。对于jQuery,您有两个选项,您可以使用它们作为案例的灵感。未经测试的代码:1.回调函数
提供回调函数:
您的插件代码:
2.插件方式
您可以定义可以调用的插件方法:
型
您的插件代码: