Greets everyone,
Update: Solved it by a workaround, changing:
$numcflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numcflinfparr['moveable_id']);
while($numcflsarr = mysqli_fetch_assoc($numcflsinfpres)) {
array_push($numcflinfparr['moveable_subs'],$numcflsarr);
}
to a function and used:
array_push($numcflinfparr[‘moveable_subs’], get_babylon_sub_object_data($numcflinfparr[‘moveable_id’]));
Old Text:
hope you can help me out with this:
I wanted to update JS object data by jquery (via $.ajax) by receiving PHP's mysqli_fetch_assoc array. It worked until I started using setInterval to update data every 15 minutes. Since then I get the following error: Uncaught (in promise) InternalError: too much recursion.
Also, tried only calling one object update (replacing for..of for a certain objectID), but I had no success. The error occurs on the line of $.ajax statement.
Edit: I could find that it occurs when an associative is pushed to array in this code:
function get_babylon_object_data($user,$id) {
$envobjects = array();
$envobjectsarr = mysqli_fetch_assoc(dbquery('SELECT object_id,object_user_id FROM '.$db_table['objects'].' WHERE object_id='.$id));
if(($envobjectsarr['object_id'] ?? 0)>0) {
$envobjectsarr['object_moveables'] = array();
if($envobjectsarr['object_user_id']==$user['id']) {
$numcflinfpres = dbquery('SELECT moveable_id,moveable_user_id,moveable_object_id,moveable_subobject_id FROM '.$db_table['moveable'].' WHERE moveable_object_id='.$envobjectsarr['object_id']);
while($numcflinfparr = mysqli_fetch_assoc($numcflinfpres)) {
$numcflinfparr['moveable_subs'] = array();
$numcflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numcflinfparr['moveable_id']);
while($numcflsarr = mysqli_fetch_assoc($numcflsinfpres)) {
array_push($numcflinfparr['moveable_subs'],$numcflsarr);
}
array_push($envobjectsarr['object_moveables'],$numcflinfparr);
}
}
$envobjectsarr['object_subobjects'] = array();
$envsubobjectsres = dbquery('SELECT * FROM '.$db_table['subobjects'].' WHERE subobject_object_id='.$envobjectsarr['object_id']);
while($envsubobjectsarr=mysqli_fetch_assoc($envsubobjectsres)) {
$envsubobjectsarr['subobject_moveables'] = array();
if($envsubobjectsarr['subobject_user_id']==$client['id']) {
$numflinfpres = dbquery('SELECT moveable_id,moveable_user_id,moveable_object_id,moveable_subobject_id FROM '.$db_table['moveable'].' WHERE moveable_subobject_id='.$envsubobjectsarr['subobject_id']);
while($numflinfparr = mysqli_fetch_assoc($numflinfpres)) {
$numflinfparr['moveable_subs'] = array();
$numflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numflinfparr['moveable_id']);
while($numflsinfparr = mysqli_fetch_assoc($numflsinfpres)) {
array_push($numflinfparr['moveable_subs'], $numflsinfparr);
}
array_push($envsubobjectsarr['subobject_moveables'], $numflinfparr);
}
}
$envobjectsarr['object_subobjects'][$envsubobjectsarr['subobject_object_pos']] = $envsubobjectsarr;
}
$envobjects[$id] = $envobjectsarr;
}
return $envobjects[$id] ?? array('object_id'=>$id);
}
Error: Uncaught (in promise) InternalError: too much recursion
aUpdateObject Updater.js:15
updateObject Updater.js:8
interval Updater.js:35
(Async: setTimeout handler)
interval Updater.js:32
(Async: setInterval handler)
initUpdater Updater.js:30
setup Central.js:29
onFinish Central.js:28
_decreaseWaitingTasksCount assetsManager.ts:1143
done assetsManager.ts:1176
_onDoneCallback assetsManager.ts:168
run assetsManager.ts:117
runTask assetsManager.ts:389
successHandler sceneLoader.ts:756
ImportMesh sceneLoader.ts:792
(Async: promise callback)
ImportMesh sceneLoader.ts:789
dataCallback sceneLoader.ts:552
GLTFFileLoader glTFFileLoader.ts:652
LoadFile fileTools.ts:343
onReadyStateChange fileTools.ts:457
(Async: EventListener.handleEvent)
addEventListener webRequest.ts:138
retryLoop fileTools.ts:482
requestFile fileTools.ts:485
RequestFile fileTools.ts:519
LoadFile fileTools.ts:342
_loadFile scene.ts:4416
_loadFile glTFFileLoader.ts:866
loadFile glTFFileLoader.ts:647
manifestChecked sceneLoader.ts:582
Database database.ts:69
OfflineProviderFactory database.ts:11
_LoadData sceneLoader.ts:603
ImportMesh sceneLoader.ts:765
runTask assetsManager.ts:379
run assetsManager.ts:114
_runTask assetsManager.ts:1194
load assetsManager.ts:1248
createScene Central.js:36
default Central.js:10
<anonym> index.js:5
InnerModuleEvaluation self-hosted:2325
evaluation self-hosted:2286
>jquery 128
isPlainObject
... (127x?) extend
File: Updater.js
export class Updater {
constructor(main) {
this.objects = {};
this.upobjects = {}; // update-requests-array
}
updateObject(id) {
if(!this.upobjects[id]) { // If no update-request ongoing
this.aUpdateObject(id);
this.upobjects[id] = id; // save update-request
}
}
async aUpdateObject(id) {
const aUpdater = this; // set to use in success function
$.ajax({
type: 'post',
url: 'path/to/updateData.php',
dataType:'json',
data: {user:aUpdater.data, id:id},
success : function (result) {
// Update Data, then Models and then UI
},
error: function(data) {
console.log('error');
console.log(data);
}
});
}
initUpdater() {
const updater = this;
this.interval = window.setInterval(function(){ // For every 15 minutes
let count = 1;
for(const object of Object.values(updater.objects)) { // For every object
setTimeout(function(){
updater.updateObject(object.id); // init single object update
},count*1000);
count++;
}
}, 900000);
}
}
File: Central.js
// Import BABYLON
// Import Updater.js
export default class {
constructor() {
this.canvas = document.getElementById('renderCanvas'); // Canvas
this.engine = new BABYLON.Engine(this.canvas, true, { stencil: true }); // prepare engine
this.scene = new BABYLON.Scene(this.engine); // init scene
this.createScene();
}
createScene() {
this.assetManager = new BABYLON.AssetsManager(this.scene); // Model Loader
this.updater = new Updater(this); // manage object updates
// this.updater.objects will be filled here and models loading is prepared
const central = this;
window.addEventListener('resize', function() {
central.engine.resize()
});
this.assetManager.onProgress = (remainingCount, totalCount, task) => {
console.log(remainingCount, totalCount, task.name);
}
this.assetManager.onFinish = (tasks) => { // If all 3d models are loaded
this.setup();
this.engine.runRenderLoop(function() {
central.scene.render(); // simulate scene
});
};
this.assetManager.load(); // Load 3d models
}
setup() {
this.updater.initUpdater();
}
}
File: index.js
// Import BABYLON
// Import Central.js
if (BABYLON.Engine.isSupported()) {
window.central = new Central(); // create Application
}
File: updateData.php
<?PHP
ini_set('MAX_EXECUTION_TIME', 1800);
define('CENTRAL_ROOT_DIR','..');
include_once(CENTRAL_ROOT_DIR.'/path/to/funcs.php'); // includes get_babylon_object_data()
include_once(CENTRAL_ROOT_DIR.'/path/to/config.php'); // includes DB config
dbconnect(); // connect to mysql DB
$conf = get_all_config(); // get app configuration
include_once(CENTRAL_ROOT_DIR.'/path/to/definitions.php'); // includes definitions
function get_babylon_object($user,$id) {
return json_encode(get_babylon_object_data($user,$id)); // get_babylon_object_data updates and returns $user (array) using mysqli_fetch_assoc to get DB data
}
echo get_babylon_object($_POST['user'] ?? array(),$_POST['id'] ?? 0);
dbclose(); // close DB
?>
Related
I'm currently implementing a WebSocket connection and I'm using a command pattern approach to emit some messages according to the command that users execute.
This is an abstraction of my implementation:
let socketInstance;
const globalName = 'ws'
const globalObject = window[globalName];
const commandsQueue = isArray(globalObject.q) ? globalObject.q : [];
globalObject.q = {
push: executeCommand
};
commandsQueue.forEach(command => {
executeCommand(command);
});
function executeCommand(params) {
const actions = {
create,
send
};
const [command, ...arg] = params;
if (actions[command]) {
actions[command](arg);
}
}
function send([message]) {
socketInstance.send(message);
}
function create([url]) {
socketInstance = new WebSocket(url);
}
In order to start sending messages, the user should be run:
window.ws.push('create', 'ws://url:port');
window.ws.push('send', 'This is a message');
The problem that I have is the connection is async, and I need to wait until the connection is done to continue to the next command. Is it a good idea to implement an async/await in commandsQueue.forEach or an iterator is a better approach? What other best approaches do you recommend?
The solution that I'm using right now is: I created an empty array of messages at the beginning and then every time that I call the send command I verify if the connection wasn't opened and I added to this array.
Something like that:
const messages = [];
let socketInstance;
let isConnectionOpen = false;
const globalName = "ws";
const globalObject = window[globalName];
const commandsQueue = isArray(globalObject.q) ? globalObject.q : [];
globalObject.q = {
push: executeCommand,
};
commandsQueue.forEach((command) => {
executeCommand(command);
});
function executeCommand(params) {
const actions = {
create,
send,
};
const [command, ...arg] = params;
if (actions[command]) {
actions[command](arg);
}
}
function send([message]) {
if (isConnectionOpen) {
socketInstance.send(message);
} else {
messages.push(message);
}
}
function onOpen() {
isConnectionOpen = true;
messages.forEach((m) => {
send([m]);
});
messages.length = 0;
}
function create([url]) {
socketInstance = new WebSocket(url);
socketInstance.onopen = onOpen;
}
I'm trying to call a function via a method, but I'm receiving an error stating that it's "not a function". I used to have the JS code in my HTML, but after I moved it to its current location (that utilizes a class and constructor) the functionality stopped working. As such, I think there's something I'm missing related to classes/constructors, but I'm not sure what.
index.js:
import calendarComponent from "./SiteAssets/scripts/calendar";
let isAdmin;
async function initComponents() {
isAdmin = await isAdminMember();
const { globalData, mmUser } = await globalInitProm(isAdmin);
const calendar = new calendarComponent(globalData, mmUser);
calendar.initRoutes();
document.getElementById('getFile').addEventListener('change', calendar.handleFileSelect, false); // ----- click event ----- //
}
initComponents();
calendar.js:
export default class {
constructor(globalData, mmUser) {
this.globalData = globalData;
this.isAdmin = mmUser.IsAdmin;
this.calendar = null;
}
initRoutes() {}
handleFileSelect(evt) {
console.log("handleFileSelect fired"); // works
let files = evt.target.files;
if (files.length > 0) {
this.parseAndUploadFile(files[0]); // "Uncaught TypeError: this.parseAndUploadFile is not a function"
}
}
parseAndUploadFile(file) {
console.log("trying to load excel file");
let reader = new FileReader();
reader.onload = function (e) {
// etc
};
}
}
To solve this, either use bind or use delegate callback.
export default class {
constructor(globalData, mmUser) {
this.globalData = globalData;
this.isAdmin = mmUser.IsAdmin;
this.calendar = null;
this.handleFileSelect = this.handleFileSelect.bind(this) // bind this here
}
// rest od the code
}
Lately I've been trying to implement WebRTC datachannels in Haxe, but come across a great deal of difficulty. When I use
dataChannel.send();
there appears to be no effect, despite the data channel supposedly being successfully opened.
The (extremely inefficient and messy) code I'm using looks like this:
package arm;
import haxe.Json;
import js.html.rtc.*;
import js.html.Document;
import js.html.WebSocket;
import js.html.DataElement;
#:expose
class DataChannelManager extends iron.Trait {
var user = "gobbledygook";
var first = false;
var initiator = true;
public function new() {
super();
var document = new Document();
var ws:js.html.WebSocket;
var config = {"iceServers":[{"url":"stun:stun.l.google.com:19302"}]};//temporary arrangement
//var optional:Array<Dynamic> = [{'DtlsSrtpKeyAgreement': true}, {'RtcDataChannels': true }];
// var connection:Dynamic = {
// 'optional'://try changing this to mandatory some time
// optional
// };
var peerConnection = new PeerConnection(config);
var dataChannel:js.html.rtc.DataChannel;
var ready = false;
function sendNegotiation(type, sdp) {
var json = {user:user/*, theloc:myloc*/, action: type, data: sdp};
ws.send(Json.stringify(json));
trace("Negotiation of type "+json.action);
}
var sdpConstraints = {
offerToReceiveAudio: false,
offerToReceiveVideo: false
};
var dcOpen=false;
notifyOnInit(function() {
var optionalStruct:Dynamic = {reliable: true}
dataChannel = peerConnection.createDataChannel("datachannel", optionalStruct);
dataChannel.onmessage = function(e){trace("DC message:" +e.data);};
dataChannel.onopen = function(){trace("-DC OPENED");dcOpen=true;};
dataChannel.onclose = function(){trace("-DC closed!");};
dataChannel.onerror = function(){trace("DC ERROR");};
trace("intialization!");
});
var firstfirst=true;
notifyOnUpdate(function() {
if (dcOpen) {
dcOpen=false;
trace("sending...");
dataChannel.send("stuff!");
}
if (firstfirst&&object.properties['go']) {
user=object.properties['string'];
first=true;
firstfirst=false;
// if (initiator) {
// peerConnection.createOffer(sdpConstraints).then(function (sdp) {
// peerConnection.setLocalDescription(sdp);
// sendNegotiation("offer", sdp);
// trace("SEND OFFER");
// }, function (data) {
// trace("Offer creation failure,", data);
// });
// } else {
// peerConnection.createAnswer(sdpConstraints).then(function (sdp) {
// trace("Answer made.");
// peerConnection.setLocalDescription(sdp);
// sendNegotiation("answer", sdp);
// });
// }
}
if (first) {
first=false;
ws = new WebSocket("ws://----------/*yes, there's an ip here*/:8080");
ws.onopen = function() {
trace("ws opened!");
peerConnection.onicecandidate = function(event) {
trace("ICE offer ready");
if (peerConnection==null || event ==null || event.candidate == null) return;
sendNegotiation("candidate", event.candidate);
}
if (initiator) {
trace("initiating");
// var optionalStruct:Dynamic = {reliable: true}
// dataChannel = peerConnection.createDataChannel("datachannel", optionalStruct);
// dataChannel.onmessage = function(e){trace("DC message:" +e.data);};
// dataChannel.onopen = function(){trace("-DC OPENED");dcOpen=true;};
// dataChannel.onclose = function(){trace("-DC closed!");};
// dataChannel.onerror = function(){trace("DC ERROR");};
peerConnection.createOffer(/*sdpConstraints*/).then(function (sdp) {
peerConnection.setLocalDescription(sdp);
sendNegotiation("offer", sdp);
trace("SEND OFFER");
}, function (data) {
trace("Offer creation failure,", data);
});
}
ws.onmessage = function (data) {
//var info=data.data.split()
if (data.data=="connected!") {return;}
var adata = Json.parse(data.data.substring(5));
if (adata.action=="offer") {
trace("Offer recieved.");
// var optionalStruct:Dynamic = {reliable: true}
// dataChannel = peerConnection.createDataChannel("datachannel", optionalStruct);
// dataChannel.onmessage = function(e){trace("DC message:" +e.data);};
// dataChannel.onopen = function(){trace("DC OPENED");dcOpen=true;};
// dataChannel.onclose = function(){trace("DC CLOSED");};
// dataChannel.onerror = function(){trace("DC ERROR");};
peerConnection.setRemoteDescription(/*try variations here*/ adata.data);
peerConnection.createAnswer(sdpConstraints).then(function (sdp) {
trace("Answer made.");
peerConnection.setLocalDescription(sdp);
sendNegotiation("answer", sdp);
});
}
if (adata.action=="answer") {
trace("Answer recieved.");
peerConnection.setRemoteDescription(/*try variations here*/ adata.data);
}
if (adata.action=="candidate") {
trace("ICE candidate recieved, looks like:",adata);
var soItDoesntComplain:Dynamic = adata.data;
peerConnection.addIceCandidate(soItDoesntComplain);
}
}
}
}
if (ready) {
trace("connected to net");
}
});
// notifyOnRemove(function() {
// });
}
}
You will notice a great deal of code is commented out -- I was expirementing with moving the dataChannel creation around.
For a better idea of what the issue is, here is the console output for the recieving and initiating clients, respectively:
In case you are wondering, notifyOnInit gets a function that is executed once at the beginning, and notifyOnUpdate gets a function called at a regular interval. object.properties['go'] is set by a different class when the username is given.
The JS api is basically the same (as far as I can tell, I haven't used WebRTC at all in the past), I haven't noticed any differences yet and I'm very sure that my issue is my fault and not Haxe's.
Thank you to those who answer.
this is not answer.
Anxious point.
peerCoonection.createOffer()
peerCoonection.createAnswer()
peerCoonection.setLocalDescription()
peerCoonection.setRemoteDescription()
peerCoonection.addIceCandidate()
are await is required.
In my node.js app, reading data from MSSQL using tedious, I'm calling the below every 1 second:
Fetch the data from the server (fetchStock function) and save it in temporary array
Send the data saved in the temporary array to the client using the Server-Sent Events (SSE) API.
It looks the 1 second is not enough to recall the fetchStock function before the previous call is completely executed, so I get execution errors from time to time.
I increased it to 5 seconds, but still get the same issue every once in a while.
How can I use Promise().then to be sure the fetchStock function is not re-called before the previouse call be completely executed?
var Request = require('tedious').Request;
var Connection = require('tedious').Connection;
var config = {
userName: 'sa',
password: 'pswd',
server: 'xx.xxx.xx.xxx',
options: {
database: 'DB',
rowCollectionOnRequestCompletion: 'true',
rowCollectionOnDone: 'true'
},
};
var sql = new Connection(config);
var addElem = (obj, elem)=> [].push.call(obj, elem);
var result = {}, tmpCol = {}, tmpRow = {};
module.exports = {
displayStock: function (es) {
var dloop = setInterval(function() {
if(result.error !== null)
if (es) es.send(JSON.stringify(result), {event: 'rmSoH', id: (new Date()).toLocaleTimeString()});
if(result.error === null)
if (es) es.send('connection is closed');
}, 1000);
},
fetchStock: function () {
request = new Request("SELECT ItemCode, WhsCode, OnHand FROM OITW where OnHand > 0 and (WhsCode ='RM' or WhsCode ='FG');", function(err, rowCount, rows) {
if (err) {
result = {'error': err};
console.log((new Date()).toLocaleTimeString()+' err : '+err);
}
if(rows)
rows.forEach(function(row){
row.forEach(function(column){
var colName = column.metadata.colName;
var value = column.value;
addElem(tmpCol, {colName: value})
});
addElem(tmpRow,{'item': tmpCol[0].colName, 'Whs': tmpCol[1].colName, 'Qty': tmpCol[2].colName});
tmpCol = {};
});
result = tmpRow;
tmpRow={}
});
sql.execSql(request);
}
}
I think what you need is a simple variable to check if there's already running request not Promise.
var latch = false;
// It will be called only if the previous call is completed
var doFetchStock = () => sql.execSql(new Request("SQL", (err, rowCount, rows) => {
// Your logic dealing with result
// Initializes the latch
latch = false;
});
module.exports = {
fetchStock: function () {
// Check if the previous request is completed or not
if (!latch) {
// Sets the latch
latch = true;
// Fetches stock
doFetchStock();
}
}
};
Actually I've used this kind of pattern a lot to allow some behavior only once.
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L397-L413
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L775-L797
Since javascript is mono-threaded a simple code like this should be enough on client-side
function () {
if(currentPromise != null){ // define in a closure outside
currentPromise = [..] // call to server which return a promise
currentPromise.then(function(){
currentPromise = null;
});
}
}
Using React-router-component, i have set to load my home page with a component called gigist which is as folows:
var React = require('react');
var AppStore =require('../../stores/app-store.js');
var GigsListItem = require('./giglistitem.js');
var StoreWatchMixin =require('../../mixins/StoreWatchMixin.js');
function getGigs()
{
return {items:AppStore.getGigs()}
}
var GigsList= React.createClass({
mixins:[new StoreWatchMixin(getGigs)],
componentWillMount:function()
{
this.setState(getGigs());
console.log(this.state);
},
componentDidMount:function()
{
this.setState(getGigs());
console.log(this.state);
},
render:function()
{
console.log("rendered view");
var liststyle={border:'1px solid black'};
/*
var items=this.state.items.map(function(item)
{
return <GigsListItem gig={item} />
}) */
return (
<div className="row" style={liststyle}>
{this.state.items}
</div>
)
}
});
module.exports=GigsList;
I am making an ajax call using npm reqwest,but the problem is that the view is being rendered and setting its state to undefined before completion of ajax request.
You can see it from my console log
From the console '1' is at setting initial state for component giglist,'3' is component will mount,'rendered view' is while rendering component,'2' is for the ajax request
which clearly states that data is not being set to component state after the ajax call.
I need to set state for the component after i get response from it.
I tried setting state with componentwillmount and componentdidmount but neither solve my issue.
here is my store code:
var AppDispatcher = require('../dispatchers/app-dispatcher');
var AppConstants = require('../constants/app-constants');
var reqwest = require('reqwest');
var merge = require('react/lib/merge');
var EventEmitter = require('events').EventEmitter;
var CHANGE_EVENT = "change";
var _gigs = [];
function getGigsList()
{
reqwest({url:'myapi',
type:'get',
success:function(resp)
{
console.log(2);
return resp.gigs;
//alert(JSON.stringify(_gigs));
}.bind(this)})
}
var _cartItems = [];
function _removeItem(index){
_cartItems[index].inCart = false;
_cartItems.splice(index, 1);
}
function _increaseItem(index){
_cartItems[index].qty++;
}
function _decreaseItem(index){
if(_cartItems[index].qty>1){
_cartItems[index].qty--;
}
else {
_removeItem(index);
}
}
function _addItem(item){
if(!item.inCart){
item['qty'] = 1;
item['inCart'] = true;
_cartItems.push(item);
}
else {
_cartItems.forEach(function(cartItem, i){
if(cartItem.id===item.id){
_increaseItem(i);
}
});
}
}
function _CartTotals()
{
var qty=0,total=0;
_cartItems.forEach(function(cartItem)
{
qty+=cartItem.qty;
total+=cartItem.cost * cartItem.qty;
});
return {'qty':qty,'total':total}
}
var AppStore = merge(EventEmitter.prototype, {
emitChange:function(){
this.emit(CHANGE_EVENT)
},
addChangeListener:function(callback){
this.on(CHANGE_EVENT, callback)
},
removeChangeListener:function(callback){
this.removeListener(CHANGE_EVENT, callback)
},
getCart:function(){
return _cartItems
},
getGigs:function(){
alert(JSON.stringify(getGigsList()));
return getGigsList();
},
getCartTotals:function()
{
return _CartTotals();
},
dispatcherIndex:AppDispatcher.register(function(payload){
var action = payload.action; // this is our action from handleViewAction
switch(action.actionType){
case AppConstants.ADD_ITEM:
_addItem(payload.action.item);
break;
case AppConstants.REMOVE_ITEM:
_removeItem(payload.action.index);
break;
case AppConstants.INCREASE_ITEM:
_increaseItem(payload.action.index);
break;
case AppConstants.DECREASE_ITEM:
_decreaseItem(payload.action.index);
break;
}
AppStore.emitChange();
return true;
})
})
module.exports = AppStore;
can somebody point out where i am doing wrong.I hope i am clear.Thanks in advance
You cannot use return statements in asynchronous code, i.e, in the success callback of your ajax-call.
Instead, try to check if there are already gigs in the store, in which case you return them, and if there are no gigs in the store you do a request to get some and in the success callback you set the store state to the new data and emit a change that will tell your component to try to fetch the store state again.
function getGigsList()
{
if (_gigs.length > 0) { return _gigs; } //return immediately if data exists
reqwest({url:'myapi',
type:'get',
success:function(resp)
{
console.log(2);
_.gigs = resp.gigs;
this.emit(CHANGE_EVENT);
}.bind(this)})
return _gigs; //currently an empty array. this way you don't have to check for undefined in the component, you can just iterate over it directly
}