In my nodejs project, there's a function where I have two queries to get data from. First query is dependent on a flag, if flag is true, then I've to run that query and pass its data to second query. Otherwise I've to set some hard-coded data for second query. But it does not wait for first query response and run second query. I've added callbacks to it but nothing worked for me. Here is my code snippet;
getData(req, callback) {
if(!req.flag){
lstData.push('d');// hardcoded data
return Reader.open(filename).then(reader => {
let code = reader.data;
if(!!code ){
return dao.getFirstData(code , (err, results) => {
if (err) return callback(err);
if(results && results.length > 0){
return lstData = results;
}
});
}
});
}
else{
lstData.push('d');// hardcoded data
}
let asyncFuncs = [
(callback) => {
dao.getSecondData(lstData, (err, results) => {
if (err) return callback(err);
return callback(null, { amounts: results });
});
}
];
asyncFuncs.push(callback => {
dao.thirdFunction(id, callback);
});
async.parallel(asyncFuncs, (err, results) => {
if (err) return callback(err);
let data1= results[0].amount;
let data2= results[1];
// some calculations with data1 & data2
return callback(err, finalResult);
});
}
No matter flag is true or false, getSecondData always returns data against d (hard-coded value). I am a newbie to nodejs, so please let me know what am I doing wrong.
SO I updated my code to add promise in it...here is updated code;
getData(req, callback) {
if(!req.flag){
lstData.push('d');// hardcoded data
Reader.open(filename).then(reader => {
let code = reader.data;
if(!!code ){
var prom = new Promise((resolve, reject) => {
dao.getFirstData(code , (err, results) => {
if (err) return callback(err);
if(results && results.length > 0){
let lstData = results;
return resolve(lstData);
}
});
});
prom.then((result) => {
return result;
});
}
});
}
else{
lstData.push('d');// hardcoded data
}
let asyncFuncs = [
(callback) => {
dao.getSecondData(lstData, (err, results) => {
if (err) return callback(err);
return callback(null, { amounts: results });
});
}
];
asyncFuncs.push(callback => {
dao.thirdFunction(id, callback);
});
async.parallel(asyncFuncs, (err, results) => {
if (err) return callback(err);
let data1= results[0].amount;
let data2= results[1];
// some calculations with data1 & data2
return callback(err, finalResult);
});
}
But still same response. It is not waiting for promise result.
There plenty of ways to achieve this but I would prefer http://async.io/ to perform multi function operations.
By using promise you can do,
function f1(argument){
return new Promise((resolve, reject) => {
try {
data = // do your thing
resolve(data)
}
catch(err){
reject(err)
}
})
}
and then use await to achieve this
async function caller(){
await f1()
}
Few other links to help you [here][2]
[2]: https://stackoverflow.com/questions/22442321/callback-function-example/48333938#:~:text=A%20callback%20function%2C%20is%20a,or%20executed)%20inside%20the%20otherFunction.&text=Without%20thinking%20too%20much%2C%20see%20the%20following%20example.&text=Generally%2C%20JavaScript%20allows%20function%20as%20a%20parameter.
You can use async/await for asynchronous calls or you can uses promises. This will wait at the asynchronous calls and once you get the response your code will execute the dependent subsequent calls.
getData(req, callback) {
if(!req.flag){
lstData.push('d');// hardcoded data --> this might be an issue as you are already pushing data.
// these are async calls where you can ask your code to wait
return Reader.open(filename).then(reader => {
let code = reader.data;
if(!!code ){
var promise = dao.getFirstData(code , (err, results) => {
if (err) reject(err);
if(results && results.length > 0){
lstData = results;
resolve(lstData);
}
});
return promise --> you can subscribe this using then
}
});
}
else{
lstData.push('d');// hardcoded data
}
let asyncFuncs = [
(callback) => {
dao.getSecondData(lstData, (err, results) => {
if (err) return callback(err);
return callback(null, { amounts: results });
});
}
];
}
To get hands on this you can check the ways of using async/await
https://javascript.info/async-await
How can I use the answer of a promise outside of. Then what should I do?
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
console.log(resp)
})
.catch(e => console.log(e))
}
});
console.log (resp) inside the .then () knows it and prints correctly, but when I want to know resp out of the forEach to use below, it says undefined
Thanks.
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
logMyData(resp);
})
.catch(e => console.log(e))
}
});
logMyData=(x)=>{
console.log(x);
}
This is just as simple as adding a helper function which executes inside your .then
Guessing that you want to be able to access the value within the forloop. Since self.campoSelects is a promise we can use async await.
// Call campo selects
function getCampoSelects(_self, tablaCampo) {
return new Promise(async (resolve, reject) => {
let campoData;
try {
campoData = await _self.campoSelects(tablaCampo);
} catch (err) {
reject(err);
}
resolve(campoData);
});
}
function happyLittleFunc() {
const arreglo = [];
arreglo.forEach(async (item) => {
if (item.tipoCampo === 3) {
let campoSelect;
// Unsure if you are setting self somewhere but it can be passed in here.
try {
campoSelect = await getCampoSelects(self, item.tipoCampo);
} catch (err) {
console.log(err);
return;
}
console.log(campoSelect);
}
});
}
happyLittleFunc();
I'm trying to develop a relative simple test on NodeJS 11.6.x. I'm not really a developer, but sometimes try to do some coding.
My objective is to create a SQLite database and repeat some steps every time a run the code:
1. Drop a table if it exists
2. Create a table
3. Insert N lines
4. Count how many lines is in the database
5. Close the database
I tried first with a basic approach using callback, but couldn't figure out a way to make the step 3 (insert N lines) and looking for a solution, the promise async/await 'pattern' sounded the way to accomplish everything.
But, after refactoring the code, the step 1 (drop table) isn't running and I still not being able to execute step 3 (insert N lines) and have no idea what is happening. I also tried to use a promise package with no luck.
Could someone please have a look and help on this and if possible, explain and or give some advice?
Thanks in advance
Edited: Well, I'not used to post here at SO and don't know the 'right' way to update things here. I beleave I should had left the first code as reference, buy I don't have anymore.
Now I think I'm almoust there. All steps are executing in order. Just the step 3 (insert N lines) that I'm not able to make it work. Or it inserts and stops not going to the next '.then' or it just insert 1 line and I'm cannot visualize what is happening.
In the code, I commented in two lines with 'BUG 1:' and 'BUG 2:'.
If I both are commented, I get what is happening, it inserts only 1 line and don't continue the promise chain
If I comment BUG 1 and let BUG 2 active, it inserts just one line and continues. I think I understand why
If I comment BUG 2 and let BUG 1 active, it inserts all lines but don't continue and again, I think I understand why
If I uncomment both (the way I think should work. Don't work, and return an aditional error "Segmentation fault"
Bellow the code:
const sqlite3 = require('sqlite3')
let db = new sqlite3.Database('./test.db');
waitTime = 1
process.stdout.write('Starting...\n')
var test = new Promise((resolve, reject) => {
process.stdout.write('Drop Table... ');
db.run(`DROP TABLE IF EXISTS test`, (err) => {
if (err) {
process.stdout.write(`Dropping Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(`Dropped!\n`)
resolve()
}, waitTime)
}
})
})
test.then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Create Table... ')
db.run(`CREATE TABLE IF NOT EXISTS test (data TEXT)`, (err) => {
if (err) {
process.stdout.write(`Creating Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(`Created!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Insert Line... ')
lines = 10
let loop = (async () => {
for (let i = 0; i < lines; i++) {
await new Promise(resolve =>
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
throw (err)
} else {
setTimeout(() => {
// process.stdout.write(`Line ${i} Inserted!\n`)
process.stdout.write(`, ${i+1}`)
resolve() // BUG 1: if this line is commented, comment it, it will insert only 1 line
}, waitTime)
}
})
)
}
})()
process.stdout.write(`, IDone\n`)
resolve() // BUG 2: If this line is commented, the promise chain stops here
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Count Line(s)... ')
db.all(`SELECT COUNT(*) AS totalLines FROM test`, [], (err, rows) => {
if (err) {
process.stdout.write(`Count Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(` ${rows[0].totalLines} Count!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Select Line(s)... ')
db.all('SELECT data FROM test', [], (err, rows) => {
if (err) {
process.stdout.write(`Select Error ${err.message}\n`)
reject()
} else {
rows.forEach((row) => {
console.log(row.data);
})
setTimeout(() => {
process.stdout.write(`${rows[0].totalLines} Select!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Close DB... ')
db.close((err) => {
if (err) {
process.stdout.write(`Closing Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(`Closed!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
console.log('Finished')
})
After the great explanation from #CertainPerformance (Thanks a lot), I was able to get it running. I believe it is now "the right" way to do it. May be there are some better ways, but for now, it is ok for me, bellow the final code:
const sqlite3 = require('sqlite3')
let db = new sqlite3.Database('./test.db');
lines = 10
process.stdout.write('Starting... ')
var test = new Promise((resolve, reject) => { process.stdout.write(`Promise Created...!\n`)
resolve()
})
test.then(() => { process.stdout.write('Drop Table... ')
return new Promise((resolve, reject) => {
db.run(`DROP TABLE IF EXISTS test`, (err) => {
if (err) {
reject(err)
} else { process.stdout.write(`Dropped!\n`)
resolve() }
})
})
}).then(() => { process.stdout.write('Create Table... ')
return new Promise((resolve, reject) => {
db.run(`CREATE TABLE IF NOT EXISTS test (data TEXT)`, (err) => {
if (err) {
reject(err)
} else {
process.stdout.write(`Created!\n`)
resolve() }
})
})
}).then(() => { process.stdout.write('Insert Line... ')
let insertLoop = (async () => {
for (let i = 0; i < lines; i++) {
await new Promise(resolve =>
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
reject(err)
} else { ( i == 0 ) ? process.stdout.write(`${i + 1}`) : process.stdout.write(`, ${i + 1}`)
resolve() }
})
)
}
process.stdout.write(`, Inserted!\n`)
})()
return insertLoop
}).then(() => { process.stdout.write('Count Line(s)... ')
return new Promise((resolve, reject) => {
db.all(`SELECT COUNT(*) AS totalLines FROM test`, [], (err, rows) => {
if (err) {
reject(err)
} else { process.stdout.write(` ${rows[0].totalLines} Counted!\n`)
resolve()
}
})
})
}).then(() => { process.stdout.write('Close DB... ')
return new Promise((resolve, reject) => {
db.close((err) => {
if (err) {
reject(err)
} else { process.stdout.write(`Closed!\n`)
resolve()
}
})
})
}).then(() => {
console.log('Finished')
}).catch((err) => {
process.stdout.write(`The process did not finish successfully: ${err}`)
})
There are two main issues. First, in the second .then, you declare loop as an async function that is immediately invoked: this means that loop will resolve to a Promise. The trimmed code looks like:
}).then(() => {
return new Promise((resolve, reject) => {
let loop = (async () => {
// do some asynchronus stuff
})()
resolve() // BUG 2
})
}).then(() => {
Declaring a Promise alone will not cause the current thread to wait for it. The above code doesn't work as expected for the same reason that this code prints after immediately:
console.log('start');
const prom = new Promise((resolve) => {
setTimeout(resolve, 500);
});
console.log('after');
You must call .then on a Promise (or await the Promise) in order to schedule additional operations after the Promise completes. Or, if you're currently inside a .then, you can return the Promise, which will mean that the next .then will run as soon as the returned Promise resolves:
}).then(() => {
let loop = (async () => {
// do some asynchronus stuff
})();
return loop;
}).then(() => {
// this block will run once `loop` resolves
Note the lack of a new Promise((resolve... constructor above - inside a .then, just returning the next Promise is often the preferred way to go, since it means a lot less code and avoids an antipattern.
The other issue with the current code is that errors will not be caught. For example, if your
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
throw (err)
// else call resolve()
throws an error, the Promise that's currently being awaited at that point will never resolve, nor will it reject - it will remain pending and unfulfilled forever. You should pass reject as the second argument to the Promise constructor, and call it when there's an error (instead of throw), for example:
await new Promise((resolve, reject) => {
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
reject(err)
} else {
// ...
This way, the awaited Promise will get rejected, which means that the whole loop will reject, and if the loop is returned, it'll allow a .catch to catch the error, for example:
var test = new Promise((resolve, reject) => {
// ...
});
test.then(() => {
return new Promise(...
// ...
})
.then(() => {
return new Promise(...
// ..
})
.then(() => {
return new Promise(...
// ..
})
.catch((err) => {
process.stdout.write(`The process did not finish successfully:`, err)
// handle errors
});
Note that, unless each db. function call needs to execute serially, it would be better to make all the requests at once, and resolve once each request has finished - this can significantly reduce the time required for the script to run. Create an array of Promises for each asynchronous call, then call Promise.all on that array to get a Promise that resolves when all of those Promises are done (or, rejects as soon as one of those Promises rejects). For example, for the second .then:
}).then(() => {
process.stdout.write('Insert Line... ')
const proms = Array.from(
{ length: lines },
(_, i) => new Promise((resolve, reject) => {
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
reject(err)
} else {
setTimeout(() => {
// process.stdout.write(`Line ${i} Inserted!\n`)
process.stdout.write(`, ${i+1}`)
resolve()
}, waitTime);
}
});
})
);
return Promise.all(proms);
}).then(() => {
Nothing else in your code looks to deal with asynchronous loops, luckily.
You may also consider a utility function like Promisify which will turn callback-based functions to Promises without all the extra new Promise(... boilerplate every time there's an asynchronous call.
Further improvements can be made by promisifying db into reusable functions, and leveraging the full power of async/await instead of mixing it with then:
const sqlite3 = require('sqlite3')
let db = new sqlite3.Database('./test.db');
function runDbAsync(sql) {
return new Promise((resolve, reject) => {
db.run(sql, (err) => {
if (err) reject(err);
else resolve();
});
});
}
function getDbAsync(sql, val) {
return new Promise((resolve, reject) => {
db.all(`SELECT COUNT(*) AS totalLines FROM test`, [], (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
function closeDbAsync() {
return new Promise((resolve, reject) => {
db.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
function write(text) {
process.stdout.write(text);
}
function writeLn(text) {
write(text + "\n");
}
async function main() {
const lines = 10
writeLn('Starting... ')
write('Drop Table... ');
await runDbAsync(`DROP TABLE IF EXISTS test`);
writeLn(`Dropped!`);
write('Create Table... ');
await runDbAsync(`CREATE TABLE IF NOT EXISTS test (data TEXT)`);
writeLn(`Created!`);
write('Insert Line... ');
for (let i = 0; i < lines; i++) {
await runDbAsync(`INSERT INTO test (data) VALUES ('a')`);
write( i == 0 `${i + 1}` : `, ${i + 1}`);
}
writeLn(`, Inserted!`);
write('Count Line(s)... ')
const rows = getDbAsync(`SELECT COUNT(*) AS totalLines FROM test`, []);
writeLn(` ${rows[0].totalLines} Counted!`)
write('Close DB... ');
await closeDbAsync();
writeLn(`Closed!`);
}
main().then(() => {
console.log('Finished')
}, err => {
writeLn(`The process did not finish successfully: ${err}`)
});
I have a collection accounts structured as follow:
Now I have a user who owns two accounts:
How do I make a query to fetch the account of this user and return it as a resolution of a promise?
Here is what I tried. It returns []
getAccounts(user_id, userRef) {
return new Promise((res, rej) => {
this.db.runTransaction((transaction) => {
return transaction.get(userRef.doc(user_id)).then((userDoc) => {
let accounts = []
if (!userDoc.exists) {
throw "User Document does not exist!";
}
let userData = userDoc.data()
let accountList = userData.accounts
for (var id in accountList){
transaction.get(this.ref.doc(id)).then(ans => {
accounts.push(ans.data())
}).catch(e => {
console.log(e)
})
}
return accounts
}).then(function (ans) {
res(ans)
}).catch((e) => {
console.log(e)
});
}).catch(function (err) {
console.error(err);
rej(err)
});
})
}
You don't need to use a transaction, since you are just reading some documents. Since you want to execute, in parallel, two (or more) asynchronous methods which return a promise (i.e. the two get() for the accounts docs) you should use Promise.all().
Something along the following lines should work:
getAccounts(user_id, userRef) {
return db.collection('users').doc(user_id).get() //replaced since I am not sure what is this.db in your case
.then(userDoc => {
const promises = []
if (!userDoc.exists) {
throw "User Document does not exist!";
}
let userData = userDoc.data()
let accountList = userData.accounts
for (var id in accountList){
promises.push(db.collection('accounts').doc(id).get())
})
return Promise.all(promises)
})
.then((results) => {
return results.map(doc => doc.data());
})
.catch(err => {
....
});
}
Note that I have used the "classic" definition for the DocumentReferences (i.e. db.collection('users').doc(user_id) and db.collection('accounts').doc(id)) since I am not 100% sure what are this.ref and this.db in your case. Just adapt as you wish!
You may also rework it with return new Promise((res, rej) => {}) as you wish but the overall philosophy remains the same.
I'm using firestore to retrieve data which has the following DS.
I have a Company collection which contains a subcollection Branches
So I'm trying to retrieve to list all the Companies with its Branches
Code:
exports.findAll = function (req, res) {
getcompanies().
then((companies) => {
console.log("Main "+ companies) // info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
return res.json(companies);
})
.catch((err) => {
console.log('Error getting documents', err);
});
}
function getCompanies(){
var companiesRef = db.collection('companies');
return companiesRef.get()
.then((snapshot) => {
let companies = [];
return Promise.all(
snapshot.forEach(doc => {
let company = {};
company.id = doc.id;
company.company = doc.data();
var branchesPromise = getBranchesForCompanyById(company.id);
return branchesPromise.then((branches) => {
company.branches = branches;
companies.push(company);
if(snapshot.size === companies.length){
console.log("companies - Inside" + JSON.stringify(companies)); //This prints all companies with its branches
}
return Promise.resolve(companies);
})
.catch(err => {
console.log("Error getting sub-collection documents", err);
return Promise.reject(err);
})
})
)
.then(companies => {
console.log("Outside " + companies) // This is never executed
return companies;
})
.catch(err => {
return err;
});
})
.catch(err => {
return err;
});
}
function getBranchesForCompanyById(id){
var branchesRef = db.collection('companies').doc(id).collection('branches');
let branches = [];
return branchesRef.get()
.then(snapshot => {
snapshot.forEach(brnch => {
let branch = {};
branch.id = brnch.id;
branch.branch = brnch.data();
branches.push(branch);
})
return branches;
})
.catch(err => {
return err;
})
}
I've all the data needed at this point.
console.log("companies - Inside" + JSON.stringify(companies)); //This prints all companies with its branches
But the then of Promise.all is never executed. So getting this error -
info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
console.log("Main "+ companies) // info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
I feel I have followed all the rules specified here: https://stackoverflow.com/a/31414472/2114024 with respect to nested promises, not sure where I'm missing the point.
Thanks in advance!
I see at least 2 problems:
forEach likely doesn't return anything, and you send the result of forEach into Promise.all().
If Promise.all() throws an exception, some of your catch handlers just grab the error and return it. Returning it turns it into a non-exception.
You also really don't have to add a catch to every Promise chain, as long as you feed the result of a Promise chain back into another promise chain, you probably only need 1 catch block.
Also one of your then() functions should not be nested as deeply. Just move it a level up, that's the point of promises.
In your code, you can use map instead of forEach. Promise.all accept an array of promises but forEach does not return an array
return Promise.all(
snapshot.map(doc => {
let company = {};
company.id = doc.id;
company.company = doc.data();
var branchesPromise = getBranchesForCompanyById(company.id);
return branchesPromise.then((branches) => {
company.branches = branches;
companies.push(company);
if (snapshot.size === companies.length) {
console.log("companies - Inside" + JSON.stringify(companies)); //This prints all companies with its branches
}
return Promise.resolve(companies);
})
.catch(err => {
console.log("Error getting sub-collection documents", err);
return Promise.reject(err);
})
})
)
Based on inputs from Evert and Rahul, thanks to both of you, I have resolved the problem here.
I handled all the error in the catch block
Promise.all was not returning anything so I converted the forEach to map.
So this is my updated code, which solves the problem:
exports.findAll = function (req, res) {
getcompanies().
then((companies) => {
console.log("Main " + companies) // Prints all companies with its branches
return res.json(companies);
})
.catch((err) => {
console.log('Error getting documents', err);
return res.status(500).json({ message: "Error getting the all companies" + err });
});
}
function getCompanies() {
var companiesRef = db.collection('companies');
return companiesRef.get()
.then((snapshot) => {
let companies = [];
return Promise.all(
snapshot.docs.map(doc => {
let company = {};
company.id = doc.id;
company.company = doc.data();
var branchesPromise = getBranchesForCompanyById(company.id);
return branchesPromise.then((branches) => {
company.branches = branches;
companies.push(company);
if (snapshot.size === companies.length) {
console.log("companies - Inside" + JSON.stringify(companies));
return companies;
}
})
.catch(err => {
console.log("Error getting sub-collection documents", err);
throw new Error(err);
})
})
)
.then(companies => {
console.log("Outside " + companies); // Executed now
return companies[companies.length - 1];
})
.catch(err => {
throw new Error(err);
});
})
.catch(err => {
throw new Error(err);
});
}
function getBranchesForCompanyById(id) {
var branchesRef = db.collection('companies').doc(id).collection('branches');
let branches = [];
return branchesRef.get()
.then(snapshot => {
snapshot.forEach(brnch => {
let branch = {};
branch.id = brnch.id;
branch.branch = brnch.data();
branches.push(branch);
})
return branches;
})
.catch(err => {
throw new Error(err);
})
}