RangeError: Maximum call stack size exceeded from unknown reason - javascript

A little intro: I actually got the problem fixed as of writing this, now I would just want to know, why it was there in the first place:
I'm trying to retrieve data from mongoDB with mongoose. The code works without problems 99% and for some reason only finding data from certain table WITH certain function causes "Maximum call stack size exceeded"-error.
Now I have jasmine tests which should confirm that the datalayerdatas-table works fine, but when it retrieves the data through getData.js, it throws the error.
Code in stack-order.
gameData-spec.js (jasmine spec):
getData.getDataLayer(dataToFind)
.then(function(data) {
console.log("DATA getDataLayer", data[0]);
})
.catch(function(e) {
console.log("Error in finding dataLayerData", e.stack);
});
getData.js (proxy / API functionality to get the data):
ret.getDataLayer = function getData(data) {
console.log(666)
return getData(dataLayerData.getDataByPlayer, {gameID: data.gameID, playerID: data.playerID, turnID: data.turnID});
};
function getData (DBfunc, data) {
var promise = DBfunc(data);
return promise;
}
gameData.js (retrieves data from mongoDB):
dataLayerData: (function dataLayerData () {
var ret = {};
ret.insert = function (data) {
return insertData( models.dataLayerData, data);
};
ret.getDataByPlayer = function (data) {
var ObjectId = require('mongoose').Types.ObjectId;
return getData( models.dataLayerData, { gameID: ObjectId(String(data.gameID)), playerID: data.playerID, turnID: data.turnID } );
};
return ret;
})()
For the other tables I didn't even need the mong.Schema.Types.ObjectId-specification, when finding data, but I tried it with this one, didn't have any effect though (basically I need it when manually doing mongoDB from command line so I assume I would need it here too).
Also the mongoDB Schema:
var dataLayerData = new mong.Schema({
"playerID": Number,
"gameID": mong.Schema.Types.ObjectId,
"turnID": Number,
"objects": [
{
"dataName": String,
"dataType": String,
"objects": {
}
}
]
});
Now as said, there are other tables being retrieved exactly the same way and they work. Even if I replace the gameData-spec.js.call with some other database-find, it works without problems. I do not see why it even gets into a callback-loop and how.
For debugging and clearing the problem I set the console.log(666) to getData.js and it keeps spamming the console for a while. So for some reason it seems that function gets called over and over.
Later on I noticed that the getData call was unnecessary (that is a stripped down version). Why does just that call in between cause a maximum call stack error?

The problem is that getData inside getDataLayer is not the getData function defined below, is actually the getData function callee above, used for recursion. Recursion can cause overflow in JavaScript.
// fixed ---v---
ret.getDataLayer = function(data) {
console.log(666)
return getData(dataLayerData.getDataByPlayer, {gameID: data.gameID, playerID: data.playerID, turnID: data.turnID});
};

Related

Unable to add async / await and then unable to export variable. Any help appreciated

Background: Been trying for the last 2 day to resolve this myself by looking at various examples from both this website and others and I'm still not getting it. Whenever I try adding callbacks or async/await I'm getting no where. I know this is where my problem is but I can't resolve it myself.
I'm not from a programming background :( Im sure its a quick fix for the average programmer, I am well below that level.
When I console.log(final) within the 'ready' block it works as it should, when I escape that block the output is 'undefined' if console.log(final) -or- Get req/server info, if I use console.log(ready)
const request = require('request');
const ready =
// I know 'request' is deprecated, but given my struggle with async/await (+ callbacks) in general, when I tried switching to axios I found it more confusing.
request({url: 'https://www.website.com', json: true}, function(err, res, returnedData) {
if (err) {
throw err;
}
var filter = returnedData.result.map(entry => entry.instrument_name);
var str = filter.toString();
var addToStr = str.split(",").map(function(a) { return `"trades.` + a + `.raw", `; }).join("");
var neater = addToStr.substr(0, addToStr.length-2);
var final = "[" + neater + "]";
// * * * Below works here but not outside this block* * *
// console.log(final);
});
// console.log(final);
// returns 'final is not defined'
console.log(ready);
// returns server info of GET req endpoint. This is as it is returning before actually returning the data. Not done as async.
module.exports = ready;
Below is an short example of the JSON that is returned by website.com. The actual call has 200+ 'result' objects.
What Im ultimately trying to achieve is
1) return all values of "instrument_name"
2) perform some manipulations (adding 'trades.' to the beginning of each value and '.raw' to the end of each value.
3) place these manipulations into an array.
["trades.BTC-26JUN20-8000-C.raw","trades.BTC-25SEP20-8000-C.raw"]
4) export/send this array to another file.
5) The array will be used as part of another request used in a websocket connection. The array cannot be hardcoded into this new request as the values of the array change daily.
{
"jsonrpc": "2.0",
"result": [
{
"kind": "option",
"is_active": true,
"instrument_name": "26JUN20-8000-C",
"expiration_timestamp": 1593158400000,
"creation_timestamp": 1575305837000,
"contract_size": 1,
},
{
"kind": "option",
"is_active": true,
"instrument_name": "25SEP20-8000-C",
"expiration_timestamp": 1601020800000,
"creation_timestamp": 1569484801000,
"contract_size": 1,
}
],
"usIn": 1591185090022084,
"usOut": 1591185090025382,
"usDiff": 3298,
"testnet": true
}
Looking your code we find two problems related to final and ready variables. The first one is that you're trying to console.log(final) out of its scope.
The second problem is that request doesn't immediately return the result of your API request. The reason is pretty simple, you're doing an asynchronous operation, and the result will only be returned by your callback. Your ready variable is just the reference to your request object.
I'm not sure about what is the context of your code and why you want to module.exports ready variable, but I suppose you want to export the result. If that's the case, I suggest you to return an async function which returns the response data instead of your request variable. This way you can control how to handle your response outside the module.
You can use the integrated fetch api instead of the deprecated request. I changed your code so that your component exports an asynchronous function called fetchData, which you can import somewhere and execute. It will return the result, updated with your logic:
module.exports = {
fetchData: async function fetchData() {
try {
const returnedData = await fetch({
url: "https://www.website.com/",
json: true
});
var ready = returnedData.result.map(entry => entry.instrument_name);
var str = filter.toString();
var addToStr = str
.split(",")
.map(function(a) {
return `"trades.` + a + `.raw", `;
})
.join("");
var neater = addToStr.substr(0, addToStr.length - 2);
return "[" + neater + "]";
} catch (error) {
console.error(error);
}
}
}
I hope this helps, otherwise please share more of your code. Much depends on where you want to display the fetched data. Also, how you take care of the loading and error states.
EDIT:
I can't get responses from this website, because you need an account as well as credentials for the api. Judging your code and your questions:
1) return all values of "instrument_name"
Your map function works:
var filter = returnedData.result.map(entry => entry.instrument_name);
2)perform some manipulations (adding 'trades.' to the beginning of each value and '.raw' to the end of each value.
3) place these manipulations into an array. ["trades.BTC-26JUN20-8000-C.raw","trades.BTC-25SEP20-8000-C.raw"]
This can be done using this function
const manipulatedData = filter.map(val => `trades.${val}.raw`);
You can now use manipulatedData in your next request. Being able to export this variable, depends on the component you use it in. To be honest, it sounds easier to me not to split this logic into two separate components - regarding the websocket -.

Getting undefined error while setting a state

I am using API to extract data from Amazon API and I am using react js for it.
The problem is when I extract them and save it in the state it gives me an error of undefined, however, I am getting the result from the API. The function takes time to get data from API, I am using a method of extracting data which is suitable in node js but also working in react and I am getting a result, but unable to save it in a state. Here's the code below:
let getReport={
path:'/',
query:{
Action:'GetReport',
ReportId:'14941942615018036',
Version:'2009-01-01',
}
};
let temp=null;
mws.request(getReport, function(e, result) {
temp=result;
this.setState({amazon_data:result}) // gives undefined error
});
I think the reason is the function execution takes time and it runs the setstate line which makes the result null, any suggestions on how to fix this?
That happens because this.setState is not part of the this scope inside your function.
let getReport={
path:'/',
query:{
Action:'GetReport',
ReportId:'14941942615018036',
Version:'2009-01-01',
}
};
let temp=null;
const _this = this;
mws.request(getReport, function(e, result) {
temp=result;
_this.setState({amazon_data:result}) // this works because _this references the outer this
});
That should work or you can use arrow functions like below.
let getReport={
path:'/',
query:{
Action:'GetReport',
ReportId:'14941942615018036',
Version:'2009-01-01',
}
};
let temp=null;
mws.request(getReport, (e, result) => {
temp=result;
this.setState({amazon_data:result}) // this works because the arrow function binds the outer this to the function
});

How to use getMapping function of Elasticsearch JS

I am trying to use this getMapping function seen here in the api. I am trying to get the mapping for an index in my database. So far I've tried this
var indexMap = client.indices.getMapping(['indexName'], function() {
console.log(indexMap);
});
and
var indexMap = client.indices.getMapping({index: 'indexName'}, function() {
console.log(indexMap);
});
both tries fail and log { abort: [Function: abortRequest] }
So I took a closer look at the ElasticSearch JS Quick Start docs to see how they were using the methods. I was confused by the API because I thought it was supposed to take an array client.indices.getMapping([params, [callback]]). But I now understand that it takes an object with params inside that object and then returns the response in a callback. The function does not return anything relevant as far as I can tell. Heres the code I used to get the mapping on 'myIndex' index. The mapping is stored in the response object.
Code:
client.indices.getMapping({index: 'patents'}, function(error, response) {
if (error) {
console.log(error);
} else {
console.log(response);
}
});

Why does creating and deleting an Azure Table fail?

I am wondering why trying to run the following test suite fails when I try to delete the table I have stored entities in. The error I get is the following
1) Azure Storage cloud storage operations "after all" hook:
Error: The specified resource does not exist. RequestId:3745d709-fa5e-4a2b-b517-89edad3efdd2
Time:2013-12-03T22:26:39.5532356Z
If I comment out the actual insertion of data it fails every other time, and if I try to do the insertion of data it fails every time with an additional "The table specified does not exist.".
For the first case this seems to indicate that there is some kind of delay in the table creation, so in every other test it is successful, and for the second case it seems to indicate that even though my callbacks are being called after table creation, the table(s) still aren't ready for data insertion.
The test suite and associated code looks like this:
describe('cloud storage operations', function () {
var storage;
before(function (done) {
this.timeout(5000);
storage = AzureStorage.usingTable('TEST', done);
});
after(function (done) {
storage.deleteTable(done);
});
it('should store without trouble', function (done) {
storage.save(factory.createChangeSet()).then(done, done);
});
});
... // snipped from azure.js
var AzureStorage = function (storageClient, tableName, callback) {
assert(storageClient && tableName && partitionKey, "Missing parameters");
this.storageClient = storageClient;
this.tableName = tableName;
var defaultCallback = function (err) { if (err) { throw error; } };
this.storageClient.createTableIfNotExists(this.tableName, function () {
callback();
} || defaultCallback);
};
AzureStorage.usingTable = function (tableName, callback) {
return new AzureStorage(
azure.createTableService(accountName, accountKey)
, tableName
, callback
);
};
AzureStorage.prototype.deleteTable = function (callback) {
this.storageClient.deleteTable(this.tableName, callback);
};
I've hit this using the c# library as well but I'm pretty sure the error message indicated the table could not be created because an operation was still in process for a table of the same name. Thinking of the backend supporting storage, it makes sense that it would not be instant. The table needs to be removed from the 3 local replicas as well as the replicas in the paired data center.
With that kind of async operation, it is going to be challenging to build up an tear them down fast enough for tests.
A workaround might be to increment a value appended to the "TEST" table name that would be unique to that test run.

JavaScript Seems to Stop After AJAX Call

The question title is rather vague, but here's my situation. I have roughly 700+ lines of jQuery for a web application, each function and "major point of interest" in the code noted by a log to the console when it fires. For example, I have a few functions that use an AJAX call to a servlet to retrieve some information. I log when the AJAX request begins, if it's succeeded (then print what data it gathered), etc. So, by the look of what my console has logged when I open the page, it seems to stop after the first AJAX call. Granted, the call seemed to work just fine, and the data it returned was perfect. As you'll see, it even populated the select box as intended. However, the console logs stop shortly after, making me believe that for some reason, the other functions are not being called...
jQuery
$(document).ready(function() {
Initialize();
});
function Initialize() {
console.log("Initializing...");
User();
Widgets();
if($.cookie("fogbugzId") != null) {
console.log("Stored ID: " + $.cookie("fogbugzId"));
$("#userSelect").val($.cookie("fogbugzId")).trigger("change");
$("#userSelect").hide();
} else console.log("No ID Stored!");
}
function User() {
console.log("Initializing User...");
$.each(FetchUsers(), function(index, user) {
$("#userSelect").append($("<option>").val(user.id).text(user.name));
});
$("#userSelect").change(function() {
if($("#userSelect").val() != "") {
console.log("User Changed to " + $("#userSelect").val() + ": " + $("#userSelect").text());
$.cookie("fogbugzId", $("#userSelect").val(), { expires: 365 });
}
Update();
});
console.log("User Initialized!");
}
function FetchUsers() {
console.log("Loading Users...");
$("#loading").show();
$.get(servlet, { command: "getUsers" }, function(data) {
var users = new Array();
$(data).find("user").each(function() {
users.push({
id: $(this).find("id").text(),
name: $(this).find("name").text()
});
});
$.each(users, function(index, user) {
console.log(">> " + user.id + ": " + user.name);
});
console.log("Users Loaded!");
return(users);
}, "xml").complete(function() {
$("#loading").hide();
}).error(function() {
console.log("Loading Users Failed!");
});
}
function Widgets() {
console.log("Initializing Widgets...");
// More Code
console.log("Widgets Initialized!");
}
Console
Initializing...
Initializing User...
Loading Users...
>> 267: Alex Molthan
>> 35: Bill Brinkoetter
>> 100: Bob Yoder
>> 189: Brian Cutler
>> 559: Brian Ormond
>> 400: Corey Nakamura
Users Loaded!
But the logging stops right there. So the AJAX call to fetch the users from the database works fine, but apparently the User() function doesn't manage to finish properly. The only error that the JavaScript console gives me is one within my jquery.min.js file:
Uncaught TypeError: Cannot read property 'length' of undefined jquery.min.js:16
f.e.extend.each jquery.min.js:16
User modifytime.js:14
Initialize modifytime.js:3
(anonymous function) modifyTime.jsp:21
f.extend._Deferred.e.resolveWith jquery.min.js:16
f.e.extend.ready jquery.min.js:16
f.c.addEventListener.B jquery.min.js:16
It looks as though it is breaking on the $.each() that iterates through the array of users returned by the FetchUsers() function. I know the function returns usable array, so I'm not sure what it's getting stuck on. Can anyone see something I'm missing right off the bat? I tried assigning the users[] returned by the FetchUsers() function into a variable first, then passing that into the $.each(), but it still didn't work. Any suggestions?
Edit: After replacing the minified version of jQuery with the uncompressed version, it seems as though the array of users that I pass into the $.each() function has now .length property, which is why it's breaking. Just to check, before I call that particular $.each() function, I placed a log of the users[].length returned from the FetchUsers() function to see that it still had no .length property. I then went to the FetchUsers() function itself and placed a log of the users[].length just before I return it. This log, however, works perfectly fine (though my example doesn't show it, it returns 40 users). So is my users[] not being returned as an array or something?
FetchUsers does not return anything, it does not even have a return statement. Additionally, $.get is an asynchronous function, so you cannot return the value it passes to its callback from the FetchUsers function. Instead, you could make FetchUsers take a callback it calls when it has received data of a user (and in this case doing that change would be relatively trivial):
function User() {
console.log("Initializing User...");
FetchUsers(function(user) { // Changed!
$("#userSelect").append($("<option>").val(user.id).text(user.name));
});
<...>
}
function FetchUsers(callback) { // Changed!
console.log("Loading Users...");
$("#loading").show();
$.get(servlet, { command: "getUsers" }, function(data) {
//var users = new Array(); No longer necessary.
$(data).find("user").each(function() {
callback({ // Changed!
id: $(this).find("id").text(),
name: $(this).find("name").text()
});
});
<...>
}
Changing those three lines with the "Changed!" comment should be enough to make it work correctly. (Though your logging of the users gotten will need to be slightly altered as they are no longer pushed into an array.)
I confess I have not read and understood every part of your source (nor checked if all the braces are closed), but FetchUsers clearly does NOT return anything (contrary to your claim) - so a call to FetchUsers() evaluates to 'undefined'. Fixing it will require some rewriting as in Javascript you cannot really return a result of asynchronous operation (like $.get) from a synchronous function (like FetchUsers()) - this would require multithreading (some kind of blocking, waiting etc).

Categories