I have code like this:
Promise.all(venue.map(venue => {
return Promise.all(concat_all.map(tgl => {
pool.query("INSERT INTO peminjaman_venue VALUES (?,?,?,?,?,?,?,?,?,?,?)",
[id_event, venue, nama_lengkap_peminjam, jabatan_nim_peminjam, jumlah_personel,
id_google_calendar, waktu_mulai_rutin, waktu_selesai_rutin, tgl,
tgl, fasilitas_lain],
function (err, rows, fields) {
if (err) throw err;
})
}))
}).then(
req.flash('message_success', 'Berhasil mengajukan event'),
res.redirect('/pengajuan_event'))
.catch(
req.flash('message_err', 'Gagal mengajukan event'),
res.redirect('/pengajuan_event')
))
The code returns error Can't set header after they are sent, that indicates the res.redirect() is called multiple times. But the code works. The data inserted to the db successfully. I changed the code below and the code just doesnt work at all.
Promise.all(venue.map(venue => {
return Promise.all(concat_all.map(tgl => {
pool.query("INSERT INTO peminjaman_venue VALUES (?,?,?,?,?,?,?,?,?,?,?)",
[id_event, venue, nama_lengkap_peminjam, jabatan_nim_peminjam, jumlah_personel,
id_google_calendar, waktu_mulai_rutin, waktu_selesai_rutin, tgl,
tgl, fasilitas_lain],
function (err, rows, fields) {
if (err) throw err;
})
}))
}).then(() = >{
req.flash('message_success', 'Berhasil mengajukan event')
res.redirect('/pengajuan_event'))
}
.catch((err) => {
req.flash('message_err', 'Gagal mengajukan event')
res.redirect('/pengajuan_event')
}
)
)
I would create an array to hold all your async requests. So
const promises = [];
promises.push(async request 1);
promises.push(async request 2);
...
Promise.all(promises).then(result => {
// do something ...
})
I would also refactor the code a bit to use async/await to remove some of those brackets. It is hard to read with all those nested promises.
Related
I am trying to chain data from an API request together and would like to gather promises from two of the blocks into a third then.
The pattern is as follows:
sql.connect(config.properties).then(pool => {
return pool.request()
.execute('stored_proc')
.then(response => { res.send(response) })
.catch(err => { res.send(err) })
.then((response, err) => { someFunction(response, err) }) // bundle here
.finally(() => sql.close())
})
How can I pass response and err into the second then block to pass into a function?
I would recommend calling the someFunction in the two locations where those values are actually available:
return pool.request()
.execute('stored_proc')
.then(response => {
res.send(response);
someFunction(response, null);
})
.catch(err => {
res.send(err);
someFunction(null, err);
})
.finally(() => sql.close())
However, given the difference between .then(…, …) and .then(…).catch(…) I would in fact recommend
return pool.request()
.execute('stored_proc')
.then(response => {
res.send(response);
someFunction(response, null);
}, err => {
res.send(err);
someFunction(null, err);
})
.finally(() => sql.close())
Now if you really want to pass the values into a following then callback, you can simply return them. Use an array to transport two values:
return pool.request()
.execute('stored_proc')
.then(response => {
res.send(response);
return [response, null];
}, err => {
res.send(err);
return [null, err];
})
.then(([response, err]) => {
someFunction(response, err);
})
.finally(() => sql.close())
Using async/await, that is now included with Node.js.
You could do something like ->
sql.connect(config.properties).then(async pool => {
try {
let response = null; //let's set to null, in case it doesn't even get that far
try {
response = await pool.request().execute('stored_proc');
res.send(response);
someFunction(response, null);
} catch (err) {
res.send(err);
someFunction(response, err);
}
} finally {
sql.close();
}
})
The advantage here is that we can keep a closure on the response, I've set it to null also, because in theory it might not even get as far as getting a response.
What make async/await do nice, it that you can go back to thinking in a sync way, but the code still runs async, so all normal try / catch / finally work as you would expect, for loops work as you would imagine etc. Just be careful of Array.forEach, and thing's that take callbacks, as that might not work the way you expect.
I know this question may already be asked. But I didn't understand how things are worked.that is why I am creating the new thread.
con.query(sql,[req.params.quizId],(err,rows,fields)=>{
//rows contains questions
if(err) throw err;
else{
let object={};
rows.forEach((item,index)=>{
object=item;
//here iam passing question id to get choices a async function
getChoices(item.id)
.then(data=>{
object.choices=data;
//save the question array
response.push(object);
//res.send(response);
});
})
res.send(response) //return empty array
}
});
function getChoices(questionId) {
let sql='SELECT id,text FROM `question_choices` where question_id=?';
return new Promise((resolve, reject) => {
con.query(sql,[questionId],(err,rows,fields)=>{
if(err) throw err;
else {
resolve(rows);
}
})
})
}
I tried several things but none is worked. I think for loop didn't wait for the promise to complete and it sends the response directly. Some async problems are happening there.
I can able to get all questions from database and for each question I need to get corresponding choices that I want.
something like this
[{id:'xx', text:'yy',choices:[{id:'c',text:'kk']},etc]
forEach runs synchronously. You're looking for Promise.all, which accepts an array of Promises, and resolves to an array of the resolved values once all of the Promises resolve. To transform your rows array to an array of Promises, use .map.
Also, when there's an error, you should call reject so that you can handle errors in the consumer of the Promise (the con.query callback), otherwise, when there's an error, it'll hang forever without you knowing about it:
con.query(sql,[req.params.quizId],(err,rows,fields)=>{
if(err) throw err;
Promise.all(rows.map((item) => (
getChoices(item.id)
.then((choices) => ({ ...item, choices }))
)))
.then((response) => {
res.send(response);
})
.catch((err) => {
// handle errors
})
});
function getChoices(questionId) {
const sql='SELECT id,text FROM `question_choices` where question_id=?';
return new Promise((resolve, reject) => {
con.query(sql,[questionId],(err,rows,fields)=>{
if(err) reject(err);
else resolve(rows);
});
});
}
Where should I call module.export, I assume, it's supposed to be a callback function.
But I'm confused as to where am I supposed to call the callback function.
I'm still confused with the solution, too complicated for me.
sql.connect(config, function(err) {
if (err)
console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('select part_num,qty from CRM.CRM.Fishbowl_Inventory where not location = \'Shipping\'',
function(err, recordset) {
if (err)
console.log(err)
// send records as a response
var details = recordset;
});
});
module.exports = details;
Confusion:
Extremely sorry to bother you guys but I want to be sure that I'm doing no harm to our database by involving any database request through Javascript.
I'm testing directly with our production database, hence cautious
So as Max provided in his answer the following code
const connectToSql = require('./connectToSql');
connectToSql()
.then(details => {
console.log(details);
//Here I can do as much logic as I want
//And it won't affect my database or call multiple requests on my DB
})
.catch(err => {
console.log(err);
});
I can understand I'm asking super silly questions, very sorry about that.
You can't export the result of your function. You want to export a function that will return your value. Like this:
function connectToSql(config) {
return new Promise((resolve, reject) => {
sql.connect(config, function (err) {
if (err) {
console.log(err);
reject(err);
}
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('select part_num,qty from CRM.CRM.Fishbowl_Inventory where not location = \'Shipping\'',
function (requestErr, recordset) {
if (err) {
console.log(requestErr);
reject(requestErr);
}
resolve(recordset);
});
});
});
}
module.exports = connectToSql;
Because your function is async, I returned a promise that will return your result. Also, your second error from your query is named the same as your first error from the connection. That would cause problems.
Example of how to use this:
const connectToSql = require('./connectToSql');
connectToSql()
.then(details => {
console.log(details);
})
.catch(err => {
console.log(err);
});
I am learning how to use Promise without libraries. From what I have read, I could chain Promise together and then add .catch in the end for error handling.
What do I expect
So if I change the URL to some false url, shouldn't I be catching the error and stop the entire program to be continuing?
What Am I seeing now?
When I put a false url, the program just throws out an error, instead of handling it like an rejection.
const request = require("request");
new Promise((resolve, reject) => {
request(
"http://maps.googleapis.com/maps/api/geocode/json?address=321%20i%20st%20davis",
(err, res, body) => {
if (err) {
reject("bad call on geo code!");
}
resolve(JSON.parse(body).results[0].geometry.location);
}
);
})
.then(res => {
const {lat, lng} = res;
return new Promise((resolve, reject) => {
request(
`https://api.darksky.net/forecast/6fb416a8313aabd902a22558e07cc032/${lat},${lng}`,
(err, res, body) => {
if (err) {
reject("bad call on darksky");
}
resolve(JSON.parse(body));
}
);
});
})
.then(res => {
const currentTemp = res.currently.temperature;
const feelTemp = res.currently.apparentTemperature;
const temps = {currentTemp, feelTemp};
return new Promise((resolve, reject) => {
request(
"http://ron-swanson-quotes.herokuapp.com/v2/quotes",
(err, res, body) => {
if (err) {
reject("bad call on quotes");
}
resolve({temps, body});
}
);
});
})
.then(res => {
console.log(
`Today's weather is ${res.temps.currentTemp}, and it feels like ${res
.temps
.feelTemp}! \nAnd here is your stupid quote of the day: \n${JSON.parse(
res.body
)[0]}`
);
})
.catch(err => {
console.log(err);
});
Error Message:
This isn't really meaningful, basically the error did not stop the program, which just passed down to the next promise. That promise receive the error but could not parse it because it is not in expected JSON format.
SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse (<anonymous>)
at Promise.then.then.then.res (/Users/leoqiu/reacto/playground/6_promiseMethod.js:48:74)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
When you call reject() inside your if statement, you don't return and you don't use an else so your resolve(JSON.parse(body).results[0].geometry.location); still gets executed and that throws an exception.
You can change to this:
new Promise((resolve, reject) => {
request(
"http://maps.googleapis.com/maps/api/geocode/json?address=321%20i%20st%20davis",
(err, res, body) => {
if (err) {
reject("bad call on geo code!");
return;
}
resolve(JSON.parse(body).results[0].geometry.location);
}
);
})
It is a common mistake that people think reject() works like break or some other control flow statement because reject() is a type of promise control flow. But, it doesn't stop execution in your block so you need to either return after it or use an else.
Or, I prefer to use if/else because I think it makes the logic even more obvious:
new Promise((resolve, reject) => {
request(
"http://maps.googleapis.com/maps/api/geocode/json?address=321%20i%20st%20davis",
(err, res, body) => {
if (err) {
reject("bad call on geo code!");
} else {
resolve(JSON.parse(body).results[0].geometry.location);
}
}
);
})
Based on Patrick Evans suggestion...
reject does not stops the program from running, so the error message gets pass down to the next Promise, which is why is throwing a json parsing error.
The solution is simply put a return in the rejection.
if (err) {
reject("bad call on geo code!");
return err;
}
This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 6 years ago.
Hello I am new to promises and callbacks in node js
I am doing something to fetch users list using another function using callbacks but getting fail.
somewhere I found to use promises. but never used promises.
can anyone help me with code?
send_noti('12', function(res){
console.log(res);
});
function send_noti(value, callback){
connection.query(" SELECT * from users ", function( err, res ){
callback( res );
});
}
You're looking for something like this:
function sendNotiAsync(value) {
return new Promise((resolve, reject) => // Promise constructor
connection.query("SELECT * from users", (err, data) => // Perform the action
err ? reject(err) : resolve(data))); // If error exists, reject, else resolve
}
And use it like this:
sendNotiAsync(someValue)
.then(data => {
// Work with data here, for example
console.log(data);
})
.catch(err => {
// Handle errors here
console.error(err);
});
In node callbacks of async functions should always have the signature function(err, result /*,...*/)
Your example should look like:
send_noti('12', function(err, res) {
if( !err ) {
console.log(res);
}
});
function send_noti(value, callback) {
connection.query(" SELECT * from users ", callback );
}
Beside that your example does not use Promises. With Promises it would look like this:
send_noti('12')
.then(function(res) {
console.dir(res)
})
.catch(function(err) {
console.dir(err)
})
function send_noti(value, callback) {
return new Promise(function(resolve, reject) {
try {
connection.query(" SELECT * from users ", function(err, res) {
if (err) {
reject(err);
} else {
resolve(res);
}
});
} catch (err) {
reject(err);
}
})
}