Create method chain before object exists - javascript

Let's say I'm using knex to run queries against an SQL database. I chain a few methods to build the query.
For example:
const sqlConfig = require('./sql.config');
var knex = require('knex')(sqlConfig);
knex.select("*")
.from("books")
.where("author", "=", "José Saramago")
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(() => {
knex.destroy();
})
Now, my question is:
Is there a way to store the method chain before the knex object is created and call it later when it is created?
Something like this:
const methodChain = <<<
.select("*"),
.from("books"),
.where("author", "=", "José Saramago")
>>>
const sqlConfig = require('./sql.config');
var knex = require('knex')(sqlConfig);
knex
.methodChain()
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(function() {
knex.destroy();
})

You could create a function that accepts the initial parameter in the chain:
function methodChain(in) {
return in.select("*")
.from("books")
.where("author", "=", "José Saramago");
}
methodChain(knex)
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(function() {
knex.destroy();
})

Sure.
const methodChain = (x) => x
.select("*"),
.from("books"),
.where("author", "=", "José Saramago");
then later
methodChain(knex)
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(function() {
knex.destroy();
})

Related

How to return promise in case of multiple transactions?

I am trying to learn to work with firestore transactions.
Here is my code:
const handleSubmit = (e) => {
e.preventDefault();
console.log(message);
let mergedId = loggedUserId + toBeContactedUserId;
let loggedUserRef = db.collection('users').doc(loggedUserId);
let toBeContactedUserRef = db.collection('users').doc(toBeContactedUserId);
let messageDbRef = db.collection('messages').doc();
db.runTransaction((transaction) => {
transaction.get(loggedUserRef) //First transaction
.then(userDoc => {
let userData = {
...userDoc.data(),
contacts: {
...userDoc.data().contacts,
[toBeContactedUserId]: {
...userDoc.data().contacts[toBeContactedUserId],
lastMsg: message,
unreadMsg: 0
}
}
}
loggedUserRef.set(userData);
})
transaction.get(toBeContactedUserRef) //Second transaction
.then(userDoc => {
let unreadMsgInc = userDoc.data().contacts[loggedUserId].unreadMsg + 1;
let userData = {
...userDoc.data(),
contacts: {
...userDoc.data().contacts,
[loggedUserId]: {
...userDoc.data().contacts[loggedUserId],
lastMsg: message,
unreadMsg: unreadMsgInc
}
}
}
toBeContactedUserRef.set(userData);
})
transaction.get(messageDbRef) ////Third transaction
.then(msgDoc => {
messageDbRef.set({
from: loggedUserId,
to: toBeContactedUserId,
content: message,
reaction: false,
seen: false,
searchId: mergedId,
time: firebase.firestore.FieldValue.serverTimestamp()
})
}
)
})
.then(res=>{
console.log(res);
})
.catch(err=>{
console.log(err);
})
}
handleSubmit() is the function which is invoked upon clicking a button.
In the first transaction.get() I am doing a write operation. In the second transaction.get() I am doing a read and a write operation and in the third, I am doing a write operation.
When I am running the code I get error as: Error: Transaction callback must return a Promise
I am not clear if I am using transactions the right way. Is there a way I can write all this logic in a single transaction.get()
Please guide me on how to resolve this error.
You can use Promise.all to wrap all three promises:
db.runTransaction((transaction) => {
const p1 = transaction.get(loggedUserRef)
.then(userDoc => {
...
})
const p2 = transaction.get(toBeContactedUserRef)
.then(userDoc => {
...
})
const p3 = transaction.get(messageDbRef)
.then(msgDoc => {
...
})
return Promise.all([p1, p2, p3]); // 👈
})
.then(res=>{
console.log(res);
})
.catch(err=>{
console.log(err);
})
Alternatively, you can use async / await to have the compiler generate that for you and get rid of some of the nesting:
db.runTransaction((transaction) => async { // 👈
let userDoc = await transaction.get(loggedUserRef);
...
userDoc = await transaction.get(toBeContactedUserRef);
...
const msgDoc = await transaction.get(messageDbRef)
...
})
.then(res=>{
console.log(res);
})
.catch(err=>{
console.log(err);
})

Delete docs from Firebase

I have an array that contains the documents id of the firebase. I need to click on the button to delete these documents in the firebase.
deletePosts() {
db.collection("users")
.doc(user.email)
.collection("posts")
.doc(this.selectedPosts[0].id)
.delete()
.then(() => {
console.log("Success!");
})
.catch(err => {
console.log(err);
});
}
},
How can I iterate documents and delete them?
You could use a batched write as follows:
deletePosts() {
let batch = db.batch();
this.selectedPosts[0].forEach(element => {
batch.delete(db.collection("users").doc(user.email).collection("posts").doc(element.id));
});
batch.commit()
.then(() => {
console.log("Success!");
})
.catch(err => {
console.log(err);
});
}
Note that a batched write can contain up to 500 operations. In case you foresee that you could have to delete more than 500 you could use Promise.all(), as follows:
deletePosts() {
const promises = [];
this.selectedPosts[0].forEach(element => {
promises.push(db.collection("users").doc(user.email).collection("posts").doc(element.id).delete());
});
Promise.all(promises);
.then(() => {
console.log("Success!");
})
.catch(err => {
console.log(err);
});
}

Async and Await not working in Axios React

i have a problem:
I want that my axios make the requistion and after it makes the this.setState with the result saved in a variable.
My code:
componentDidMount() {
let mails = [];
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, () => {
this.state.employees.map(i => {
async axios.get(`/api/status/${i.mail}`)
.then(res => {
mails.push(res.data)
await this.setState({
mails: mails
})
})
.catch(err => console.log(err))
})
}))
.catch(err => console.log(err))
}
But it gives error syntax.
Best explanation: I want saved all results of the map in the variable mails and later to use the setState to changes the result of just a time.
Someone could tell me where i'm wandering? Please.
You are using async await at the wrong places. async keyword must be used for a function that contains asynchronous function
await keyword needs to be used for an expression that returns a Promise, and although setState is async, it doesn't return a Promise and hence await won't work with it
Your solution will look like
componentDidMount() {
let mails = [];
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, async () => {
const mails = await Promise.all(this.state.employees.map(async (i) => { // map function contains async code
try {
const res = await axios.get(`/api/status/${i.mail}`)
return res.data;
} catch(err) {
console.log(err)
}
})
this.setState({ mails })
}))
.catch(err => console.log(err))
}
It's not a good practice to mix async/await with .then/.catch. Instead use one or the other. Here's an example of how you could do it using ONLY async/await and ONLY one this.setState() (reference to Promise.each function):
componentDidMount = async () => {
try {
const { data: employees } = await axios.get('/api/employee/fulano'); // get employees data from API and set res.data to "employees" (es6 destructing + alias)
const mails = []; // initialize variable mails as an empty array
await Promise.each(employees, async ({ mail }) => { // Promise.each is an asynchronous Promise loop function offered by a third party package called "bluebird"
try {
const { data } = await axios.get(`/api/status/${mail}`) // fetch mail status data
mails.push(data); // push found data into mails array, then loop back until all mail has been iterated over
} catch (err) { console.error(err); }
})
// optional: add a check to see if mails are present and not empty, otherwise throw an error.
this.setState({ employees, mails }); // set employees and mails to state
} catch (err) { console.error(err); }
}
This should work:
componentDidMount() {
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, () => {
this.state.employees.map(i => {
axios.get(`/api/status/${i.mail}`)
.then( async (res) => { // Fix occurred here
let mails = [].concat(res.data)
await this.setState({
mails: mails
})
})
.catch(err => console.log(err))
})
}))
.catch(err => console.log(err))
}
You put async in the wrong place
async should be placed in a function definition, not a function call
componentDidMount() {
let mails = [];
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, () => {
this.state.employees.map(i => {
axios.get(`/api/status/${i.mail}`)
.then(async (res) => {
mails.push(res.data)
await this.setState({
mails: mails
})
})
.catch(err => console.log(err))
})
}))
.catch(err => console.log(err))
}

Node.js unable to run mysql query inside loop

I have two tables in mysql and want to query a table depending on the result of another, so I wrote a function like
export function getLocations(req, res) {
const database = new Database();
database.query('select * from districts')
.then(rows => {
let appData = [];
rows.forEach(row => {
const new_database = new Database();
new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
res.status(200).json(appData); //final result here
database.close()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
}
Here I want to get run second query based for each of the row of first query.
I am getting an empty array as result. My first query is executing properly and I logged to see all rows are being returned. What could be the issue?
You can make it work by making this async
rows.forEach(async row => {
const new_database = new Database();
await new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
The operation you are performing is I/O and JS is single threaded. It means in layman terms it will not wait will iterating your loop where it is going to be making a request where there is some wait while the request processes. You need to tell JS that this event is asynchronous. For this you need to use async/await
Guides
forEach async/await
async/await MDN documentation
I don't have the environment in this machine. it may have some errors but you can fix it if it have, take a look at the following logic
export function getLocations(req, res) {
const database = new Database();
const promises=[];
database.query('select * from districts')
.then(rows => {
let appData = [];
rows.forEach(row => {
promises.push(getAnotherQuery(row));
});
database.close()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
return Promise.all(promises).then(result)=> res.status(200).json(result); //final result here
}
getAnotherQuery=(row)=>{
return new Promise((resolve,reject)=>{
const new_database = new Database();
const appData=[]
new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
resolve(appData);
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
}

Using callback function inside array.map javascript

I am trying to bcrypt password for every user in an array.
router.post("/insertuser", (req, res) => {
var promises = users.map((item) => {
bcrypt.genSalt(10)
.then((salt) => {
return item
})
})
Promise.all(promises)
.then((results) => {
console.log(results)
res.json({
"data": results
})
})
})//end route
But I am getting results = [undefined,undefined].
How can I return array element from bcrypt.genSalt(10).then
Please help as I am new to ES6
EDIT: My user users array is like this:
[{ "username": "admin", "admin": true}
]
Simply return the promise from bcrypt.genSalt.
router.post("/insertuser", (req, res) => {
var promises = users.map((item) => {
return bcrypt.genSalt(10)
.then((salt) => {
return item
})
})
Promise.all(promises)
.then((results) => {
console.log(results)
res.json({
"data": results
})
})
})//end route
When you add .then() after any promise it will directly get resolved. In your code users.map() will run synchronously and the promises will have undefined.
Here is the code you can use :
router.post("/insertuser", (req, res) => {
var promises = users.map((item) => {
return bcrypt.genSalt(10);
})
Promise.all(promises)
.then((results) => {
console.log(results)
});
})//
Also notice that salt is used to generate hash. You are only generating salt. To generate hash of password also add bcrypt.hash(password,salt). Here is the code :
var promises = users.map((item) => {
return bcrypt.genSalt(10);
})
Promise.all(promises)
.then((results) => {
promises = results.map((item, index) => {
return bcrypt.hash(users[index], item);
});
return Promise.all(promises);
})
.then(result => {
console.log(result);
})
.catch(err => {
console.log(err);
});

Categories

Resources