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.
Related
I am very new to nodejs and I am getting the above-mentioned error for the below code. any help is appreciated. thankyou.
PushNotifications.sendMessageToAll(announcement, (err,res) => {
if (err){
return res.status(500).send()
}
res.status(200).send()
}
sendMessageToAll: function (notification, cb) {
payload = {....}
admin.messaging().subscribeToTopic(tokens,topic).then((res) =>{
return admin.messaging().sendToTopic('/topics/NATA', payload)
}).then((res) =>{
console.log('sent', res)
cb(undefined,res)
}).catch((err) =>{
console.log('Subscribed error',err)
cb(err,undefined)
})
}
}
The error you are getting is because the property status doesn’t exist on undefined that is getting passed back on this line:
cb(err,undefined)
The caller expects a response object, because it wants to use the response object to set a HTTP status code and send back a response to the browser:
return res.status(500).send()
The solution is simple: pass res (short for 'response') to the error callback, instead of undefined:
}).catch((err) =>{
console.log('Subscribed error', err)
cb(err, res)
})
You're literally passing undefined to your callback cb which expects the second parameter to be a response object
.catch((err) =>{
console.log('Subscribed error',err)
cb(err,undefined)
})
You need to make your callback be able to handle having no res to set the status.
In the catch block of your promise you call the callback without the res input:
admin.messaging().subscribeToTopic(tokens,topic).then((res) =>{
return admin.messaging().sendToTopic('/topics/NATA', payload)
}).then((res) =>{
console.log('sent', res)
cb(undefined,res)
}).catch((err) =>{
console.log('Subscribed error',err)
cb(err,undefined) // <-- HERE the second param is res!
})
}
}
EDIT
res is not defined in the catch block. A way could be:
let outerRes;
admin.messaging().subscribeToTopic(tokens,topic).then((res) =>{
outerRes = admin.messaging().sendToTopic('/topics/NATA', payload)
return outerRes;
}).then((res) =>{
console.log('sent', res)
cb(undefined,res)
}).catch((err) =>{
console.log('Subscribed error',err)
cb(err,outerRes) // <-- HERE the second param is res!
})
}
}
I'm having some trouble understanding what I'm doing wrong. I have a function that receives a url to which should make a GET request, in case of success should fill a combo with the received data (this depends which function calls it), in case of fail it should execute some common code.
getFirstCombo = () => {
this.getFromApi('/First/GetAll')
.then(data => this.setState({firstComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
getSecondCombo = () => {
this.getFromApi('/Second/GetAll')
.then(data => this.setState({secondComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
parseCombo = (data: any) => {
const combo = data.map(item => (
{ label: item.description, value: item.id }
));
return combo;
}
getFromApi = (url: string) : Promise<any> => {
return restApiAxios.get(url)
.then(response => {
return response.data;
})
.catch(error => {
console.log('ERROR: ', error);
});
}
this code is executed on the componentDidMount of the react component, but when it fails, it first prints :
ERROR: Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
and immediately after:
PanelDatos.tsx:50 ERROR2: TypeError: Cannot read property 'map' of undefined
at PanelDatos.parseCombo (PanelDatos.tsx:55)
at PanelDatos.tsx:50
so, when failing executes the catch block from getFromApi and then it tries to execute the then block in getFirstCombo, which triggers the catch block from the same function cause data does not exist, why is that? shouldnt it just execute the first catch?
thanks in advance
.catch returns a promise much like .then, allowing you to return a custom value and handle it that way.
Try doing the following to observe the effect:
Promise
.reject(1)
.catch(e => e) // Catch the error and return it
.then(console.log) // will log 1 to the console
This means you'll need to add some checks if you want to continue to use promises like this:
Promise
.reject(new Error('haha'))
.catch(err => ({err}))
.then(({err, data}) => {
if(err) return // Do nothing
// enter code here
})
However, using async / await will improve readability even more:
getFirstCombo = async () => {
let response
try {
response = await this.getFromApi('/First/GetAll')
} catch (e) {
return // Exit early
}
let parsed
try {
parsed = this.parseCombo(data)
} catch (e) {
console.log(e)
return // Exit early
}
return this.setState({firstComboOptions: parsed})
}
And, of course, throw the error again in your catch block in your api to allow it to handle api calls.
This is happening since inside getFromApi catch method on the error you are not returning anything, so by default, it is returning a resolved promise with null response and the execution goes inside getFirstCombo then method, causing another error. You can update your code to resolve this like:
getFromApi = (url: string): Promise<any> => {
return restApiAxios.get(url)
.then(response => response.data)
.catch(error => Promise.reject(error));
}
The static Promise.reject function returns a Promise that is rejected. So, it will go directly into catch of wherever getFromApi is called.
DEMO:
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => Promise.reject(err))
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
.catch(error => console.log('ERROR2: ', error));
}
getFirstCombo()
DEMO #2 (With getFirstCombo function not having any catch block) :
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => {
console.log('ERROR in getFromApi(): ', err);
return null; // return null, empty array, 0 or false... as per your requirement
})
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
// Same value set in catch block of getFromApi will return in this then() block
// Validate this `data` variable before processing it further like:
// if(data === null) this means an error had occurred
// else continue with your logic
}
getFirstCombo()
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.
I recently have learned something about fetch() and promise, and now I need to use it in project. Here I have a fetch() function, which works very well, but I think, it must catch an error. So, what is the best way to catch error in fetch() functions? And i need to catch them in both then()?
Here some code:
const endpoint = 'http://localhost:3030/api/hotels';
const promise = fetch(endpoint)
.then(res => res.json(), err => {
console.log(err);
})
.then(parseRooms, err => {
console.log(err);
})
Thank you !
Use the fact that promise handlers chain together. Each call to then or catch creates a new promise, which is chained to the previous one.
So in your case:
const promise = fetch(endpoint)
.then(res => res.json())
.then(parseRooms)
.catch(error => {
// Do something useful with the error
});
I'm assuming there that parseRooms throws an error if there's a problem with the structure it receives.
You probably want to check res.ok in there, too, since fetch only fails if there was a network error, not if there was an HTTP error such as a 404:
const promise = fetch(endpoint)
.then(res => {
if (!res.ok) {
throw new Error(); // Will take you to the `catch` below
}
return res.json();
})
.then(parseRooms)
.catch(error => {
// Do something useful with the error
});
I am communicating with a webservice and the return can be negative or porisitvo and will fall into the success of the call, but if the return is negative I need to manually throw an exception
.map(res => res.json())
.subscribe(res => {
let returno = JSON.parse(res.d);
if (returno.status == 'success') {
this._loggedIn(returno);
} else {
throw returno;
}
}, err => {
console.error('ERROR', err);
});
You can throw exceptions in JS by just passing objects to throw. So throw err in your case.
I expect you'll want all of your subscribers to handle the error in the same way? Therefore, you can handle the error at the Observable level, not the Subscriber level.
Thus, you can do something like the following:
public tryLogin(url: string): Observable<any> {
return this.http.get(url)
.map(res => res.json().d)
.map(res => {
if (res.status !== 'success') {
Observable.throw(res);
}
return res;
});
}
Then you can call this code in the following fashion:
this.tryLogin('mydata')
.subscribe(
data => this._loggedIn(data),
err => console.log(err);
)
The great thing with this approach is, if you don't want your subscribers to know what's gone on, you can abstract that and either authenticate them in the next callback, or handle the event of a failure in the error callback.