What is the fastest way to persist data in 2019? [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Update:
The problem below is trying to persist data using an asynchronous call... very inefficient and unreliable. I looked into socket.io but it seems to be ideal for messaging systems. Can any one advise of a better solution/library? The problem is, suppose I am trying to make a connection with a device, that device is streaming data every .8 sec. The data comes as a JSON object, with values timestamped. If I wanted to chart these values and create a seemingly real-time feel, what should I be considering? What I came across in my research:
socket.io
rabitQt
MQTT
Old solution below, asynchronous call is a mess, it has nothing to do with knowing JavaScript callback functions or not, I believe this is just not the way.This is why this is not quite a duplicate question to "How do I return the response from an asynchronous call? " it is not a simple callback or promise.
I'm trying to access my JSON object, so I can map out the data and manipulate it. However, I can't seem to be able to access it outside of my request function. I put my request function inside a variable and tried to call it by calling the parent's properties, but in my command line I read "undefined" what am I doing wrong?
1) I tried using the push() method inside my request function, so I could create some prototypical object that stores all the data, then contain it within a function so I could use it outside my request, but I get the error ".push() is not a function"
var r = request(options, function (error, response, xyz) {
var deviceData = JSON.parse(xyz);
if (error) throw new Error(error);
for (var i = 0; deviceData.data.length > i; i++) {
var extractedData = [];
extractedData = deviceData.data[i];
};
//this executes an entire JSON object with all the data I need
//console.log(extractedData);
});
//my console shows "undefined"
console.log(r.extractedData);
I expect to get a whole JSON object like I do when I console.log inside the request function. Instead, I get "undefined"
UPDATED
I tried using a promise as #ajaykumar has suggested, I ran into the issues described
var xyz = {};
var r = {};
Getdata().then(data => {
r["extractedData"] = data;
//console shows all the data as inside the promise block
//console.log(data)
}, err => console.log(err));
function Getdata() {
return new Promise((resolve, reject) => {
request(options, function (error, response) {
var deviceData = JSON.parse(response.body);
//console.log(xyz) at this point shows all data, message: call successful" and the data looks like this:
[console.log(xyz)][1]
//response shows the entire object too, but it is through Node.js, so you actually see my object contained by the "body" property of Node.js. I didn't think about this before. So now I modified my code and removed xyz as it is actually not necessary
if (error) reject(error);
var extractedData = [];
for (var i = 0; deviceData.data.length > i; i++) {
extractedData.push(deviceData.data[i]);
};
//the console shows all my data
//console.log(extractedData)
resolve(extractedData);
});
//the console no longer associates extractedData, displays "extractedData is undefined"
//console.log(extractedData)
});
}
//console prompts error "ReferenceError: data is not defined", if I try extractedData instead it gives me the same old message "undefined". If I try r[0] I get "undefined"
//console.log(data);

Since request will execute in asynchronous way, it will not wait for request to complete so it will execute the next line before we get response from request hence it is undefined.
Var r = {}
request(options, function (error, response, xyz) {
var deviceData = JSON.parse(xyz);
if (error) throw new Error(error);
for (var i = 0; deviceData.data.length > i; i++) { var extractedData = []; extractedData = deviceData.data[i]; };
r["extractedData"] = extractedData;
console.log(extractedData);
});
// this will execute before request gets complete hence it is undefined
console.log(r.extractedData);
So create promise
Getdata() {
return new promise ((resolve, reject) =>{
request(options, function (error, response, xyz) {
var deviceData = JSON.parse(xyz);
if (error) reject (error) ;
var extractedData = [];
for (var i = 0; deviceData.data.length > i; i++)
{
extractedData.push(deviceData.data[i]);
};
resolve (extractedData)
});
}
}
Call this function
Var r ={}
Getdata ().then(data=>{
r["extractedData"] = data;
},err=>console.log(err))
This will work for your problem

Related

How can I insert objects in SQL Server running a function in a loop? ConnectionError: .connect can not be called on a Connection in `Connecting` state

I'm working in a NodeJS project, this project I decided to change the way I'm doing it because this way wasn't working, let me try to explain it.
I need to insert data into a SQL Server DB, so I did a function insertOffice() this function opens a connection using Tedious, then fetchs data to an url with data from an array data2 to load coords, and then with this coords creates an object, then inserts this object into a DB. When inserting only one part of my data2 array, it works, by only sendind data[0] it adds:
{
latjson: 1,
lonjson: 1,
idoficina: "1",
}
But I want to insert both of the parts of my array, changing data2[0] to data2[index], to be able to insert all my array, so I tried creating another function functionLooper()that loops insertOffice() to insert my data from my array data2.
I builded this little code to learn how to loop a function, this prints index that is the value I use for bringing idoficina.
As you can see functionLooper() runs the code twice, so it can read fully data2 array, I have this little code that works with the same logic, I builded my full code using this:
function insertOffice(index) {
console.log(index);
}
function functionLooper() {
for (let i = 0; i < 5; i++) {
let response = insertOffice(i);
}
}
functionLooper();
This prints:
0
1
2
3
4
So my code it's supposed to send index
I'm expecting my code to loop my insertOffice() and being able to insert my objects, the issue is that this doesn't seems to work as I am getting this error:
C:\...\node_modules\tedious\lib\connection.js:993
throw new _errors.ConnectionError('`.connect` can not be called on a Connection in `' + this.state.name + '` state.');
^
ConnectionError: `.connect` can not be called on a Connection in `Connecting` state.
this is my code:
var config = {
....
};
const data2 = [
...
];
var connection = new Connection(config);
function insertOffice(index) {
console.log(index)
connection.on("connect", function (err) {
console.log("Successful connection");
});
connection.connect();
const request = new Request(
"EXEC SPInsert #Data1, ... ",
function (err) {
if (err) {
console.log("Couldn't insert, " + err);
} else {
console.log("Inserted")
}
}
);
console.log(myObject.Id_Oficina)
request.addParameter("Data1", TYPES.SmallInt, myObject.Id_Oficina);
request.on("row", function (columns) {
columns.forEach(function (column) {
if (column.value === null) {
console.log("NULL");
} else {
console.log("Product id of inserted item is " + column.value);
}
});
});
request.on("requestCompleted", function () {
connection.close();
});
connection.execSql(request);
}
function functionLooper() {
for (let i = 0; i < 2; i++) {
let response = insertOffice(i);
}
}
functionLooper();
I do not know if this is the right way to do it (looping the inserting function insertOffice()twice), if you know a better way to do it and if you could show me how in an example using a similar code to mine, would really appreciate it.
You're approaching an asynchronous problem as if it's a synchronous one. You're also making your life a bit harder by mixing event based async tasks with promise based ones.
For example, connection.connect() is asynchronous (meaning that it doesn't finish all its work before the next lines of code is executed), it is only done when connection emits the connect event. So the trigger for starting the processing of your data should not be started until this event is fired.
For each of the events in your loop they are not running one at a time but all at the same time because the fetch() is a promise (asynchronous) it doesn't complete before the next iteration of the loop. In some cases it may have even finished before the database connection is ready, meaning the code execution has moved on to DB requests before the connection to the database is established.
To allow your code to be as manageable as possible you should aim to "promisify" the connection / requests so that you can then write an entirely promise based program, rather than mixing promises and events (which will be pretty tricky to manage - but is possible).
For example:
const connection = new Connection(config);
// turn the connection event into a promise
function connect() {
return new Promise((resolve, reject) => {
connection.once('connect', (err) => err ? reject(err) : resolve(connection));
connection.connect()
});
}
// insert your data once the connection is ready and then close it when all the work is done
function insertOffices() {
connect().then((conn) => {
// connection is ready I can do what I want
// NB: Make sure you return a promise here otherwise the connection.close() call will fire before it's done
}).then(() => {
connection.close();
});
}
The same approach can be taken to "promisify" the inserts.
// turn a DB request into a promise
function request(conn) {
return new Promise((resolve, reject) => {
const request = new Request(...);
request.once('error', reject);
request.once('requestCompleted', resolve);
conn.execSql(request);
});
}
This can then be combined to perform a loop where it's executed one at a time:
function doInserts() {
return connect().then((conn) => {
// create a "chain" of promises that execute one after the other
let inserts = Promise.resolve();
for (let i = 0; i < limit; i++) {
inserts = inserts.then(() => request(conn));
}
return inserts;
}).then(() => connection.close())
}
or in parallel:
function doInserts() {
return connect().then((conn) => {
// create an array of promises that all execute independently
// NB - this probably won't work currently because it would need
// multiple connections to work (rather than one)
let inserts = [];
for (let i = 0; i < limit; i++) {
inserts.push(request(conn));
}
return Promise.all(inserts);
}).then(() => connection.close())
}
Finally I could fix it, I'm sharing my code for everyone to could use it and do multiple inserts, thanks to Dan Hensby, I didn't do it his way but used part of what he said, thanks to RbarryYoung and MichaelSun90 who told me how, just what I did was changing my
var connection = new Connection(config);
to run inside my
function insertOffice(index) { ... }
Looking like this:
function insertOffice(index) {
var connection = new Connection(config);
....
}

How to hold my response in nodejs until the end of my loop

My code is as follows :
myinsts.forEach(function (myinstId) {
Organization.getOrgById(myinstId,function (err,insts)
{
res.json(insts);
})
});
I'm usng Node.js and I'm getting the error "Can't set headers after they are sent" , Obviously my server sends first iteration , how can I make it hold until I get the whole data
It's not going to be pretty. Essentially what you could do is create a variable to hold the data through every iteration and then check if that's the last callback to be called. If it is, then you can output your json. Try something like the following:
var _counter = 0;
var _values = [];
myinsts.forEach(function (myinstId) {
Organization.getOrgById(myinstId,function (err,insts)
{
_values.push(insts);
if(++_counter == myinsts.length)
res.json(_values);
})
});
You would like to use Promises for this kind of works, you can execute each async function in parallel with Promise.all() and get the data in the order that is called.
var promises = [];
myinsts.forEach(function (myinstId) {
promises.push(new Promise((resolve, reject)=>{
Organization.getOrgById(myinstId,function (err,insts){
if(err) return reject(err);
resolve(insts);
});
}));
});
Promise.all(promises)
.then((allInsts)=>{res.json(allInsts)}) // all data fetched in loop order.
.catch((error)=>{console.log(error)});
Also consider to make your getOrgById handler return a Promise instead of using callback.
You have to send response just one time, not several times. Your code send the response each of iteration. Send the response once if specific condition fulfilled. Like below.
myinsts.forEach(function (myinstId, index) {
Organization.getOrgById(myinstId,function (err,insts){
if( index == myinsts.length -1 ){
res.json(insts);
}
})
});

Using promises to request order-dependent information in a for loop

I have a relatively simple task; existing information is out of date, so I have to request information from an API, modify the existing info file and push it back to the server, updated. The information is in the form of a json file; simple enough. That file contains an object with an array of objects that have several properties that must be updated. This is where the problems occur; the array of objects generates an array of API requests, whose responses must match the original object that spawned the request (since the response contains info that must be updated in the object).
This is the gist of what I've managed to do so far with promises:
function main() {
// First get existing data.
getExistingData().then(function(result) {
console.log(result); // It worked, return it for next 'then' to use.
return result;
}, function(err) {
console.log(err); // This usually never happens.
}).then(function(result) { // Use the existing data to generate the requests for new data.
requestNewData(result).then(function(moddedJson) {
console.log(moddedJson); // This happens BEFORE I get responses back from the request, which is wrong.
});
});
}
function getExistingData() {
return new Promise(function(resolve, reject) {
fetch('dataURLHere')
.then(function(res) {
resolve( res.json()); // Turn result into JSON, and return it.
})
})
}
function requestNewData(rawJson) {
return new Promise(function(resolve) {
// Loop over the number of objects in the original data.
for (var i = 0; i < rawJson.length; i++) {
// Loop over the array of objects within each object.
for (var multiId = 0; multiId < rawJson.hits.length; multiId++) {
var requestUrl = "someURLConstructedFromJsonData";
var hit = rawJson.hits[multiId];
new Promise(function(resolve) {
request(requestUrl, function(error, response, body) {
if (!error && response.statusCode == 200) {
// Need to parse the XML response into a js object.
parseString(body, function (err, result) {
hit.propertyToChange = result.propertyToChange;
hit.propertyToChange2 = result.propertyToChange2;
});
}
else {
console.log("No data for this item.");
}
resolve(hit);
});
})
}
}
resolve(rawJson);
})
}
Basically, the things I want to happen are:
1) Get original data. This is easy and accomplished by my code already.
2) Use original data to generate requests for each document in the data, and for each set of properties within each document. This is also not a problem.
3) Ensure the returning data from requests gets matched to existing data. THIS is the problem part that I can't wrap my head around.
The problem is that you're resolving too early.
The red flag is when you create a promise but never do anything with it:
new Promise(function(resolve) {
request(requestUrl, function(error, response, body) {
...
That promise does get resolved correctly, but no one is waiting on it. The simple solution is Promise.all:
function requestNewData(rawJson) {
return new Promise(function(resolve, reject) {
var promises = [];
for (var i = 0; i < rawJson.length; i++) {
...
promises.push(new Promise(function(resolve) {
...
}));
}
resolve(Promise.all(promises));
});
}
Now, Promise.all(promises) will resolve with an array of results. This might not be ideal, but you can but a then on it if you just want to wait using it:
return Promise.all(promises).then(function() {
resolve(updatedJson);
}, reject);
In this way, you could have each of the individual promises modify the response data. The promise returned by requestNewData won't resolve until all of them are done, so at that point updatedJson would be updated.
Be warned: Promise.all has fast-fail behavior. In your case, I think this is exactly what you want. But if you need to know which ones failed, or if you need to wait until all requests complete (fail or otherwise), Promise.all may not be the right thing.
PS: You should maybe reject if the request() function provides an error. Otherwise you could have holes in your data if there's a network error, without actually getting a reject.

Returning data from Parse promise chain

I think I have got my head around Parse promise chains, but what I don't understand is how I return my data from the functions (i) back up the promise chain and (ii) back to the calling method of my original JS code.
Using the code below, the first Parse query is called, then a() and finally b() which is all fine. The console.log at each stage are for test purposes and show that the chains have been executed and in order.
I now have 3 questions:
How do I get the data back from function a() and function b() so I can access it in the main function getUserCompetitionTokens()?
How do I return any data from getUserCompetitionTokens() to the main program which has called this Parse code?
What if I want the data from function a() and also from function b() to BOTH be returned to my main program?
function getUserCompetitionTokens(comp_id) {
Parse.initialize("****","****");
currentUser = Parse.User.current();
user_competition = Parse.Object.extend("UserCompetition");
var user_comp_query = new Parse.Query(user_competition);
user_comp_query.equalTo("UserParent", currentUser);
user_comp_query.find().then(a).then(b);
function a(user_comp_results) {
var no_results = user_comp_results.length;
var id = user_comp_results[0].id;
console.log("User Competition Output: " + no_results + " results found, first item id: " + id);
var Competition = Parse.Object.extend("Competition");
var query = new Parse.Query(Competition);
return query.get(comp_id, {
success: function(competition) {
console.log("COMP - " + competition.id);
},
error: function(competition, error) {
console.log(error);
}
});
}
function b(competition) {
var Competition = Parse.Object.extend("Competition");
var query = new Parse.Query(Competition);
query.get(comp_id, {
success: function(competition) {
console.log("COMP 2 - " + competition.id);
},
error: function(competition, error) {console.log(error);}
});
}
}
You don't return :)
I'm sorry, that is just a joke on a technicality: you see, Promises are a way to express asynchronous behaviour. So you can't, strictly saying, grab the return value of a function.
However, you are already grabbing the results of each step correctly... You just didn't realize you can use it yourself.
The answer is to use your own then handler.
user_comp_query.find().then(a).then(b).then(function(results){
console.log(results); // Hooray!
});
However, keep in mind that a Promise might fail. That's why it's important to pass a second handler, which will be called whenever there is an error.
var handleSuccess = function (results) {};
var handleFailure = function (error) {};
var parsePromise = user_comp_query.find().then(a).then(b);
parsePromise.then(handleSuccess, handleFailure);

Parse Javascript promises on builtin functions

Im trying to send a push message to everyone with read access every time a new note is saved. This is the code I've been trialing. Before I was experience a can't serialise objects this was because I was using asynchronous calls. After doing a bit more research I stumbled across javascript promises. I've never used something like this before so i'm finding it difficult to get my head around it.
The problem I'm finding is that object.getACL() is an asynchronous call. I tried first just adding a .then after it but this returned method not found. Now I've tried to create it as its own function and manually make a new promise and resolve the promise after it returns. This isn't working either. I just need the function to sequential call one after another, like normal programming lol.
In pseudocode it should get the ACL. Evaluate each member in the ACL and return an array of all users with read access. Then send a push notification to each member. Any help would be greatly appreciated.
Parse.Cloud.afterSave("Notes", function(request) {
var idsToSend = [];
var i = 0;
console.log("start");
var getACL = function(object) {
var promise = new Parse.Promise();
console.log("enter getACL");
var noteACL = object.getACL() {
promise.resolve(noteACL);
};
return promise;
};
getACL(request.object).then(function(objACL) {
var ACLinJSON = objACL.toJSON();
for (var key in ACLinJSON) {
if (ACLinJSON[key].read == "true") {
idsToSend[i] = key.id;
console.log("i = " + i + " = " + idsToSend[i]);
i++;
}
}
console.log(idsToSend);
//lookup installations
var query = new Parse.Query(Parse.Installation);
query.containedIn('user', idsToSend);
Parse.Push.send({
where: query,
data: {
alert: "note updated"
}
}, {
success: function() {
console.log("Success sent push");
},
error: function(error) {
console.error("can’t find user"); //error
}
});
});
});
ParseObject.getACL() is not an async call, it returns a Parse.ACL not a Parse.Promise, why bother using a promise for this? You're needlessly complicating your code.
Remove the Promises, just get the ACL and loop through the JSON, and if you still have problems, post a new question with the issues you then have.

Categories