I am writing a small ElectronJS application to interact with a database using the NodeJS implementation of SQLite3.
I am able to store data in the database, no problems.
//renderer.js
document.querySelector('#new-store-add').addEventListener('click', _ => {
console.log("Adding new Store");
let storeName = document.querySelector("#new-store-name-input").value;
ipcRenderer.send('db-add-new-store', storeName);
});
//main.js
ipcMain.on('db-add-new-store', (e, r) => {
addStore(r);
});
//storemgr.js
exports.addStore = (newStore) => {
const sql = `INSERT INTO STORE (store_name) VALUES('${newStore}')`;
db.exec(sql);
}
Problems arise when I try to read and display data from the same table.
I have tried a couple of different approaches including the current async Promise approach
//renderer.js
document.querySelector('#main-get-stores').addEventListener('click', () => {
ipcRenderer.send('db-get-all-stores');
});
ipcRenderer.on('db-got-all-stores', (e,r) => {
console.log('r -> ', r);
console.log('e -> ', e);
console.log('e.stores -> ', e.stores);
})
//main.js
ipcMain.on('db-get-all-stores', async (e,r) => { //NOTE async here
console.log("Get All Stores");
let stores = await getAllStores();
console.log('stores -> ', stores);
e.sender.send('db-got-all-stores', stores);
});
//storemgr.js
exports.getAllStores = () => {
return new Promise((resolve, reject) => { //NOTE returns a Promise
const sql = "select * from STORE";
let res = [];
db.each(sql, (err, row) => {
console.log('row -> ', row); //NOTE actually correctly logs the data to the console
res.push(row);
});
console.log('res -> ', res); //NOTE this is still an empty array
resolve(res);
});
}
The stores variable in main.js is always undefined. So is res in storemgr.js.
My question is, what is the best approach to handle the Database connection when trying to display the data as soon as I have read it?
Forget about actually displaying the data for now, I am stumped as to why the return object is always undefined after I have already read it from the database.
I also tried different db functions such as .all, the result is much the same.
Related
I'm getting myself confused on async/await as I build a simple chrome extension that returns saved data from local storage.
I don't seem to understand why my function returns undefined, but the inner function returns an object.
I might be misunderstanding promises and async/await, but I can't seem to figure this out.
// Loads saved data from local storage and sets the selected station to the saved selection
const loadSavedData = async () => {
let key = "vin6"; // key to retrieve from local storage
await chrome.storage.local.get([key], (result) => {
if (!result[key]) return;
console.log(result); // this gets printed out on the console
return result[key];
});
};
document.addEventListener("DOMContentLoaded", () => {
const data = loadSavedData();
data.then((res) => {
console.log(res); // this prints out as undefned
})
});
Your loadSavedData() function doesn't return anything (thus undefined):
const loadSavedData = async () => {
...
await chrome.storage.local.get([key], (result) => {
if (!result[key]) return;
console.log(result);
return result[key]; // You are returning from the callback, not the main function!!!
});
};
Using async/await is to avoid using promises. So why are you using data.then() in your event listener???
A solution to these problems would be the following, entirely dropping the async/await (not tested)
// Loads saved data from local storage and sets the selected station to the saved selection
const loadSavedData = () => {
let key = "vin6"; // key to retrieve from local storage
return new Promise((resolve, reject) => {
chrome.storage.local.get([key], (result) => {
if (!result[key]) return;
console.log(result);
resolve(result[key]);
});
});
};
document.addEventListener("DOMContentLoaded", () => {
const data = loadSavedData();
data.then((res) => {
console.log(res);
})
});
I've got a NodeJS/Express/MongoDB app. For one of my endpoints I'm trying to get some stats from the database and I'm having trouble doing it using promises.
I know that db doesn't get moved between the different .thens but no matter how I re-arrange the code I can't get anything out of the console.log() except the first users count. I've also tried saving db into a variable declared at the start of the function but that doesn't help either.
What's the proper way to make multiple queries to MongoDB in a promisified way?
Node Code:
function getStats(num){
var stats = "";
MongoClient.connect(`${mongoString}/SiteUsers`)
.then(db => {
db.db().collection("Users").find({}).count()
.then( userCount => {
stats += `Users: ${userCount}\n`
return db;
})
.then( adminCount => {
db.db().collection("Users").find({admin:true}).count()
.then( adminCount => {
stats += `Admins: ${adminCount}\n`
})
})
.then( data => {
console.log(`Stats are now: \n${stats}`);
})
})
.catch(err => {
if(err) console.log(err);
});
}
Thanks!
There are several ways that you can handle the order of your promises. One of which is to use async/await functionality.
async function getStats(num){
try {
const conn = await MongoClient.connect(`${mongoString}/SiteUsers`)
const userCount = await conn.db().collection("Users").find({}).count()
const adminCount = await conn.db().collection("Users").find({admin:true}).count()
console.log(`User count: ${userCount}\nAdmin count: ${adminCount}`)
} catch (err) {
console.log(err)
}
}
I am trying to use axios and express to get an array of links from a page, harvest data at each link, and display results to the user. The process I'd like to implement is:
Run axios.get(targetPage), harvest the links on the target page, save to an array
Run run axios.all to get a response from link
Harvest data from each response
Display to user
Below is my app.get function:
app.get('/search', function (req, res) {
var context = {};
context.resources = [];
var promises = [];
var year = req.query.year;
var targetPage = endpoint + year;
axios.get(targetPage).then(resp => {
var $ = cheerio.load(resp.data);
var pages = []
$('queryStr').each(function (i, ele) { pages.push(endpoint + $(ele).attr("href")) });
context.links = pages;
pages.forEach( link => promises.push( axios.get(link) ));
}).then(axios.all(promises)).then( responses => {
responses.forEach( resp => {
var resource = {};
resource.link = resp.url;
var $ = cheerio.load(resp.data)
resource.title = $('query').text()
context.resources.push(resource);
})
}).then( () => {
res.render('search', context);
})
})
I have verified that the urls in the pages[] array are valid. I have tried calling res.render after the first axios.get call, and it successfully rendered my response. I have separately tested the logic in the forEach and verified that it works for each url. I'm getting an error message at the then block immediately following axios.all, which states that responses returned by axios.all is undefined. the Here is the error message:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'forEach' of undefined
Thanks for reading.
[TLDR]
This is because the function of your first then clause doesn't have a return statement.
This means there is no successful data passed to the next then clause and so on.
then chaining requires return for each then clause.
Example
There's many problems in my original post, but the main issue was failure to return a Promise in each step of my .then chain. Below is working code, significantly refactored. I'm sure there's better ways of passing along errors and loading in parallel, so please leave answers and comments with any improvements. In addition to the resource linked to by Nazim Kerimbekov and Khoa, I recommend this post for learning how to chain promises.
app.get('/pageToRender', function (req, res) {
var context = {};
var page1 = req.query.year
getInitial(page1)
.then( pages => Promise.all( pages.map( page => { return getResource(page) })))
.then(resources => {
context.resources = resources;
res.render('pageToRender', context);
})
.catch( errors => { console.log(errors) })
})
function getInitial (page) {
return new Promise( (resolve,reject) => {
axios.get(page).then( resp => {
var pages = []
var $ = cheerio.load(resp.data)
$('.mw-content-ltr li a').each(function (i, ele) { pages.push(endpoint + $(ele).attr("href")) });
console.log(pages);
resolve(pages);
}).catch( error => reject(error))
})
}
function getResource(page) {
return new Promise ( (resolve, reject) => {
axios.get(page)
.then( response => {
var resource = {};
resource.link = page;
var $ = cheerio.load(response.data)
resource.title = $('.Title').text()
console.log("resolving resource", resource);
resolve(resource);
}).catch (error => { error })
})
}
I have a firebase cloud function as follows:
exports.foo = functions.database
.ref("/candidates/{jobTrack}/{candidateId}")
.onCreate((snap, context) => {
const candidate = snap.val().candidate;
const jobTrack = context.params.jobTrack;
const jobsRef = admin.database().ref("jobs");
return jobsRef
.child(jobTrack)
.once("value")
.then(jobs => {
const promises = [];
jobs.forEach(job => {
promises.push(job.val());
});
return Promise.all(promises);
})
.then(jobs => {
return jobs.forEach(job => {
var percent = getMatchedPercent(candidate, job);
if (percent >= 0.9) {
admin
.database()
.ref("feeds")
.child(job.feedId)
.child("upcomingWeek")
.push(candidate); // add to team's feed
}
});
})
.catch(err => {
console.log("firebase got an error: ", err);
});
});
In function foo, I call a local non-cloud function getMatchedPercent which is defined as below:
const getMatchedPercent = (candidate, job) => {
console.log("In get percent: ", candidate, job);
// do something
};
The problem is when I checked job.val() in foo before calling getMatchedPercent, I can see valid data got printed from console for job.val(). When once get in getMatchedPercent, I tried to print job, it complains it's undefined.
Is there anything I missed? Why the information of job can be lost during calling a function? Thanks!
Your problem is caused by these lines:
const promises = [];
jobs.forEach(job => {
promises.push(job.val());
});
return Promise.all(promises);
job.val() returns an object (of the data) not a promise, so Promise.all() incorrectly interprets it as a resolved promise with no value. In your next block of code, the array jobs is an array of undefined values rather than the data you were expecting.
To fix this, you would instead return the array of values rather than using Promise.all().
const jobValues = [];
jobs.forEach(job => {
jobValues.push(job.val());
});
return jobValues;
But because no asyncronous work is taking place here you can flatten your Promise chain. By doing so, you will use less memory because you won't need an array containing of all of your job.val() objects at once.
exports.foo = functions.database
.ref("/candidates/{jobTrack}/{candidateId}")
.onCreate((snap, context) => {
const candidate = snap.val().candidate;
const jobTrack = context.params.jobTrack;
const jobsRef = admin.database().ref("jobs");
return jobsRef
.child(jobTrack)
.once("value")
.then(jobs => {
const promises = []; // will contain any team feed update promises
jobs.forEach(jobSnapshot => { // This is DataSnapshot#forEach
const job = jobSnapshot.val();
const percent = getMatchedPercent(candidate, job);
if (percent >= 0.9) {
promises.push(
admin
.database()
.ref("feeds")
.child(job.feedId)
.child("upcomingWeek")
.push(candidate) // add to team's feed
);
}
});
return Promise.all(promises);
})
.catch(err => {
console.log("Failed to update team feeds: ", err);
});
});
However, this still has another problem where some of the feed updates may succeed and others may fail which leaves your database in an unknown state. So instead you might want to consider writing to the database atomically (all data is written, or nothing at all).
This could be achieved using:
exports.foo = functions.database
.ref("/candidates/{jobTrack}/{candidateId}")
.onCreate((snap, context) => {
const candidate = snap.val().candidate;
const jobTrack = context.params.jobTrack;
const jobsRef = admin.database().ref("jobs");
return jobsRef
.child(jobTrack)
.once("value")
.then(jobs => {
const pendingUpdates = {}; // "path: value" pairs to be applied to the database
const feedsRef = admin.database().ref("feeds");
jobs.forEach(jobSnapshot => { // This is DataSnapshot#forEach
const job = jobSnapshot.val();
const percent = getMatchedPercent(candidate, job);
if (percent >= 0.9) {
const pushId = feedsRef.push().key; // push() without arguments doesn't write anything to the database, it just generates a new reference with a push ID we can use.
const path = job.feedId + "/upcomingWeek/" + pushId;
pendingUpdates[path] = candidate; // queue add to team's feed
}
});
// apply all updates in pendingUpdates object,
// relative to feedsRef as an all-or-nothing operation.
// e.g. pendingUpdates["feed001/upcomingWeek/9jksdfghsdjhn"] = "someUserId"
// will be written to "feeds/feed001/upcomingWeek/9jksdfghsdjhn"
return feedsRef.update(pendingUpdates); // commit changes
})
.catch(err => {
console.log("Failed to apply all feed updates: ", err);
});
});
I'm currently trying to fill a cassandra table with the content of an xlsx file, but I'm facing a problem.
I managed to do an array with all the queries necessary to fill my table ("insert into my_table (values name, ...) values (values, ...);" ).
So my array got like 7000 strings.
I'm then doing a promise and inside this promise I made a loop to fill and array of promises in order to trigger the first promise when all the promises are over.
this is the code I made
index.js
const ImportFileContent = require("./scripts/import_file_content")
const InsertDb = require("./scripts/insertDb")
const cassandra = require('cassandra-driver');
const databaseConfig = require('./config/database.json');
const authProvider = new cassandra.auth.PlainTextAuthProvider(databaseConfig.cassandra.username, databaseConfig.cassandra.password);
const db = new cassandra.Client({
contactPoints: databaseConfig.cassandra.contactPoints,
authProvider: authProvider
});
// ImportFileContent.importFileContent return an array of string, those strings contains all the 7000+ queries
ImportFileContent.importFileContent().then(queries => {
InsertDb.clients(db, queries).then(result => {
console.log(result);
db.shutdown(function (err, result) {
});
});
});
insertDb.js
let DB = null;
module.exports = {
ClientsLeasing: function (db, queries) {
DB = db;
return insertClientsLeasing(queries);
}
}
function insertClientsLeasing(queries) {
return new Promise((resolve, reject) => {
let nbError = 0;
let nbSuccess = 0;
let promisesArray = [];
//I made i <2000 here because my cassandra setup doesn't manage more than 2048 request in parallele
for (let i = 0; i < 2000; i++) {
promisesArray.push(new Promise(function (resolve, reject) {
DB.execute(queries[i], function (err, result) {
if (err) {
nbError++;
reject(err)
} else {
nbSuccess++;
resolve();
}
});
}));
}
Promise.all(promisesArray).then((result) => {
console.log("is over")
console.log("over ===================== success => ", nbSuccess, " errors => ", nbError);
resolve("success");
}).catch((error) => {
console.log(error);
console.log("is over error")
console.log("over ===================== success => ", nbSuccess, " errors => ", nbError);
resolve("error");
});
});
}
My table has two primary keys the creation date (which is now()) and the client id that may be in multiple rows of the xlsx (that can be the source of the problem ?).
So now when I launch this code my output is
then when I do a count in the table via cqls I only get 1962 rows with this output
I feel like I'm missing something in the way I use my promises. I don't really get it.
Thanks