Async and Knex: how they work ? - mysql

I parse an json object and for each element, i need to execute many queries.
In first, a "select" query and depending on the result, i execute an insert or an update.
I would like use async.js and knex.js
The issue it's the order of execution is not the searched order
async.each(newContent,function(e){
//var e=JSON.stringify(element),
var z=-1,
devicepresenceId = e.device_presence_id;
//console.log(e);
async.waterfall([
function(cb) {
knex('jos_joomrh_event_employee_hours_presence')
.whereRaw('device_presence_id=?', devicepresenceId)
.select('id', 'applied_at', 'applied_at_end')
.debug()
.then(function (rows) {
console.log(rows);
z = _.keys(rows).length;
console.log('rows0', z);
cb(null,z);
})
.catch(function (e) {
console.log(e)
reject(e)
})
cb(null,z);
},
function(z,cb){
console.log('z',z);
if (parseInt(z)==0)
{
console.log('insertHoursPresence');
//insertHoursPresence(e)
}
else{
console.log('updateHoursPresence');
//updateHoursPresence(e)
}
cb(null,'two')
}
],
function(err,z){
if(err)console.log(err);
console.log(z);
}
)}
)}
In fact; it executed the second function and and the cb function and after the first function with knex.:
Thanks for your help
Mdouke

In the knex part, you have a call to "cb(null,z)", you have to "move" that call inside catch function (but replacing reject(e) part). Your problem is that you're calling cb() function outside knex, of course it is called immediately independent of knex result)

Related

Why it is Undefined? How can i solve it?

when i am trying to get this console.log(this.empresas[0]); it says it is undefined, but empresas is loaded in the function.
empresas: any;
constructor(...) {
this.getEmpresas();
console.log(this.empresas[0]);
}
getEmpresas(){
this.empresas = [];
this.http.get("http://url").subscribe( data => {
this.empresas = JSON.parse(data["_body"]);
}, err =>{
console.log(err);
});
}
Because just by calling getEmprass(), the value of this.emprass will not get updated. getEmprass() will immediately return without updating it. Later on, when the http request is completed, this.emprass gets updated, but that is after the time you printed its value.
Many things happen in Javascript asynchronously. Observables and subscribe function is one of them: its content gets executed asynchronously in the future (similar to callbacks).
You may wanna change your code to this:
constructor(...) {
this.getEmpresas();
console.log('hey, getEmpresas() finished!');
}
getEmpresas(){
this.empresas = [];
this.http.get("http://url").subscribe( data => {
this.empresas = JSON.parse(data["_body"]);
console.log(this.empresas[0]);//---------------------------> Print it here
}, err =>{
console.log(err);
});
}
If you want to wait in constructor function until the http request is finished, you should use an async function .

Sequelize: only the last few rows are updated

I have an array of (few thousands of) data object that I need to insert or update depending on a condition. A simple upsert() method is implemented in my model class.
Implementation
csv.upsert = async function (values, condition) {
let obj = await csv.findOne({ where: condition });
if (obj) {
// update
console.log("existing record updated")
return await obj.update(values);
} else {
// insert
console.log("new record inserted")
return await csv.create(values);
}
}
This upsert method is then used, where I loop through the array of objects to insert or update them in db.
Usage
try {
await models.sequelize.authenticate();
await models.sequelize.sync();
let dataToBeInserted = getArrayOfDatatoInsert();
dataToBeInserted.map(async function (data) {
let condition = {
'categorygroup': data.categorygroup,
'category': data.category,
'country': data.country,
'city': data.city
};
await csvModel.upsert(data, condition);
})
// await restofthestuff();
} catch (error) {
console.log("error", error);
}
For test I took a dataset where all of my data needs to be updated.
When I run this method:
I can see in the (along with sequelize log turned on) log that "existing record updated" message is printed for each and every record that exists which is desired output. Only the last few (30) data gets updated in the db. Where as it works for csv.create(values)
~ How can I update all the records and obviously not just the last 30 data, any help's appreciated. ~
EDIT: Apparently I got this to work by using csv.update(values, {where: condition}) instead of using obj.update(values).
New question: I didn't look further into the sequelize's update method but is this a bug or am I doing something wrong here?
As detailed in the commented code below, your log is after your return and so will never be executed.
Also you were not using await in an async function, so either don't make it async or use await.
csv.upsert = async function (values, condition) {
const obj = await csv.findOne({ where: condition });
// you can await here, no need for then as you are in an async function
if (obj) { // update
// you need to log before your return condition
console.log("existing record updated")
return obj.update(values);
// since we return here, the rest of the code will not be executed
// you can skip the use of else
}
// we are in this part of the code only if object is falsy
// insert
console.log("new record inserted")
return csv.create(values);
}
You could also use Promise.all to ensure all the upsert are done:
await Promise.all(dataToBeInserted.map(async function (data) {
let condition = {
'categorygroup': data.categorygroup,
'category': data.category,
'country': data.country,
'city': data.city
};
await csvModel.upsert(data, condition);
}))
this will also ensure that if an error occur it gets catched by your try / catch
Maybe that will help you find what's causing the unexpected behaviours.

How do I use promises in a Chrome extension?

What I am trying to do is create a chrome extension that creates new, nested, bookmark folders, using promises.
The function to do this is chrome.bookmarks.create(). However I cannot just
loop this function, because chrome.bookmarks.create is asynchronous. I need to wait until the folder is created, and get its new ID, before going on to its children.
Promises seem to be the way to go. Unfortunately I cannot find a minimal working example using an asynchronous call with its own callback like chrome.bookmarks.create.
I have read some tutorials 1, 2, 3, 4. I have searched stackOverflow but all the questions do not seem to be about plain vanilla promises with the chrome extension library.
I do not want to use a plugin or library: no node.js or jquery or Q or whatever.
I have tried following the examples in the tutorials but many things do not make sense. For example, the tutorial states:
The promise constructor takes one argument—a callback with two
parameters: resolve and reject.
But then I see examples like this:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
How this works is a mystery to me.
Also, how can you call resolve() when its never been defined? No example in the tutorials seem to match real life code. Another example is:
function isUserTooYoung(id) {
return openDatabase() // returns a promise
.then(function(col) {return find(col, {'id': id});})
How do I pass in col, or get any results!
So if anyone can give me a minimal working example of promises with an asynchronous function with its own callback, it would be greatly appreciated.
SO wants code, so here is my non-working attempt:
//loop through all
function createBookmarks(nodes, parentid){
var jlen = nodes.length;
var i;
var node;
for(var i = 0; i < nodes.length; i++){
var node = nodes[i];
createBookmark(node, parentid);
}
}
//singular create
function createBookmark(node, parentid){
var bookmark = {
parentId : parentid,
index : node['index'],
title : node['title'],
url : node['url']
}
var callback = function(result){
console.log("creation callback happened.");
return result.id; //pass ID to the callback, too
}
var promise = new Promise(function(resolve, reject) {
var newid = chrome.bookmarks.create(bookmark, callback)
if (newid){
console.log("Creating children with new id: " + newid);
resolve( createBookmarks(bookmark.children, newid));
}
});
}
//allnodes already exists
createBookmarks(allnodes[0],"0");
Just doesn't work. The result from the callback is always undefined, which it should be, and I do not see how a promise object changes anything. I am equally mystified when I try to use promise.then().
var newid = promise.then( //wait for a response?
function(result){
return chrome.bookmarks.create(bookmark, callback);
}
).catch(function(error){
console.log("error " + error);
});
if (node.children) createBookmarks(node.children, newid);
Again, newid is always undefined, because of course bookmarks.create() is asynchronous.
Thank you for any help you can offer.
Honestly, you should just use the web extension polyfill. Manually promisifying the chrome APIs is a waste of time and error prone.
If you're absolutely insistent, this is an example of how you'd promisify chrome.bookmarks.create. For other chrome.* APIs, you also have to reject the callback's error argument.
function createBookmark(bookmark) {
return new Promise(function(resolve, reject) {
try {
chrome.bookmarks.create(bookmark, function (result) {
if (chrome.runtime.lastError) reject(chrome.runtime.lastError)
else resolve(result)
})
} catch (error) {
reject(error)
}
})
}
createBookmark({})
.then(function (result) {
console.log(result)
}).catch(function (error) {
console.log(error)
})
To create multiple bookmarks, you could then:
function createBookmarks(bookmarks) {
return Promise.all(
bookmarks.map(function (bookmark) {
return createBookmark(bookmark)
})
)
}
createBookmarks([{}, {}, {}, {}])
.catch(function (error) {
console.log(error)
})
Take the advantage of the convention that the callback function always be the last argument, I use a simple helper function to promisify the chrome API:
function toPromise(api) {
return (...args) => {
return new Promise((resolve) => {
api(...args, resolve);
});
};
}
and use it like:
toPromise(chrome.bookmarks.create)(bookmark).then(...);
In my use case, it just works most of the time.

synchronous mysql queries in nodejs

I'm pretty new to nodejs and I'm having some difficulties to understand how to use the mysql connection object.
My problem is not in the code but in the design pattern.
lets say I have a user module
module.exports = function(){
return{
id: "",
load: function(id){
var sql = 'SELECT * from users where id = '+ DB.escape(id);
console.log(1);
DB.query(sql, function (err, rows) {
this.id = rows[0].id; // not working
console.log(rows[0].id); // prints the id 4
console.log(2);
});
console.log(3);
}
}
}
from outside the module i run the next code
var user = require('../modules/user');
var selected_user = user();
console.log("entering users me route");
selected_user.load(4);
console.log("user id is " + selected_user.id); //This does not print the id 4
when I run the code, the console logs 1, then 3, and then 2.
This is due to the asynchronous flow of node js.
But if I'm building a website, and I need the query to end in order to populate my user object before I send the HTML to the browser???
What's the right way to do it ?
Also when I try to populate the id property of user in the id i receive from the DB it does not work.
Any ideas?
Thanks
There are several ways to do this. I would go with Promises.
Suppose you have an asynchronous function "getUsers".
It looks like this:
function getUsers() {
longQuery(function(err, result){
// What to do with result?
});
You need to rewrite it to be able to use the result.
Let's try:
function getUsers() {
return new Promise(function(resolve, reject) {
longQuery(function(err, result){
if(err) reject(err)
else resolve(result)
});
});
Now this function returns a promise. What do we do with that promise?
function handleRequest(req, res) {
getUsers().then(function(result) {
// Do stuff with result
res.send(myProcessedData);
}).catch(function(err) {console.log(err)};
}
This could also have been done with callbacks, passing the response object as a parameter to the query function, and many other ways, but I think promises are a very elegant way for handling this.
this.id = rows[0].id; // not working
The above line is not working because you are setting it to this.id from inside a callback function. When you are inside a callback function this does not mean the this in the main object.
For more discussion about this: see How to access the correct `this` context inside a callback?
To tackle the asynchronous nature of javascript you can either use promise like the answer from matanso or you can pass a callback function to your load method. So your load: function(id) method will be load: function(id, callbackFunction) and call the callback function when you get all the data that you need.

NodeJS return values mySQL

i have a question regarding nodeJS.
I have a LOOP in nodeJS that iterates mySQL table and depending on the result executes a specific query.
var query = "SELECT field1,fields2,field3 FROM database.table1";
mySQLconnection.query(query, function (err, data) {
if (err)throw err;
if (data.length != 0) {
for (var i = 0; i <= data.length - 1; i++) {
doFunction1(data[i].field1, data[i].field2, data[i].field3, function (func1data) {
if (func1data == false) {
doFunction2(data[i].field1, data[i].field2, data[i].field3, function (func2data) {
if (func2data == false) {
doFunction3(data[i].field1, data[i].field2, data[i].field3, function (func1data) {
});
}
});
}
});
}
}
});
The problem with the above approach is that it will not wait for the result from the first doFunction1, but rather it will continue with the i++
Either of your two functions doFunction1 or doFunction2 intiates some kind of asynchronous calls. Looks like you are not familiar with asynchronous behaviour of javascript. You can use the async.each to handle these type of situations.
for (var i=0; i<10000; i++) {
console.log(i);
some_async_function_call(i);
}
Basically this code initiates 10000 requests to execute your asyc functions. There's no guarantee your code will be executed before the for loop is finished.
What you need to do is to chain your async function calls so it will do things sequentially. There are multiple ways to achieve this. For example async.js or bluebird.js
async.js: series, waterfall, parallel... etc
https://github.com/caolan/async#waterfall
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
bluebird.js
https://github.com/petkaantonov/bluebird
var loop = function(){
// check loop conditions, return when the loop is finished
if (finished_loop) return Promise.resolve();
return Promise.promisify(your_function)()
.then(loop)
}
why do you want callbacks here in for loop. Write straight forward code which will be executed sequentially otherwise this code executes asynchronously.
At the end i went with a array and counter, when counter reaches the array length, it would restart it self. Basically recursive function.
Thanks for your help