This question already has answers here:
What is the explicit promise construction antipattern and how do I avoid it?
(3 answers)
Closed 3 years ago.
Axios is described as Promise-based, so is there a need for returning a new Promise when using Axios to query for data?
app.get('/api/nearbyRecommendations', async (req, res) => {
if(!req.query) return res.send({ error: 'Please enable location to get recommendations.' })
try {
const { longitude, latitude } = req.query
const locationName = await location.getLocationName(longitude, latitude)
res.send(locationName)
} catch (error) {
res.send(error)
}
})
I am making a GET request to the MapBox API, but I do not seem to ever get any errors despite setting up the catch block for my Axios request, even if I throw a new Error in the .then() block.
const getLocationName = async (latitude, longitude) => {
return new Promise((resolve, reject) => {
axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${longitude},${latitude}.json?access_token=${darkSkyAPIKey}`, {json: true})
.then(response => {
if(!response.data) return reject({ error: 'No location found.' })
resolve(response.data)
}).catch(error => {
reject(error)
})
})
}
If possible, do help and point out anything that may be altered to follow best practices.
You can just return the promise immeditately without using an async function:
const getLocationName = (latitude, longitude) => {
return axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${longitude},${latitude}.json?access_token=${darkSkyAPIKey}`, {json: true})
.then(response => {
if(!response.data)
throw Error('No location found.')
return response.data;
}).catch(error => {
console.log(error);
throw error;
})
}
Axios.get already returns a promise to you. If you also define the function as async that means that the returned promise will be wrapped in a promise again. So in your example you were triple wrapping the response in a promise. If you replace it with the getLocationName function with a regular function, usage in the first code snippet will remain exactly the same.
Related
This question already has an answer here:
Google Cloud Functions - warning Avoid nesting promises promise/no-nesting
(1 answer)
Closed 3 years ago.
NOTE: this question is mainly about error handling, and if this is an ok approach, not about nesting promises, please read before closing
Since there are currently no error codes for services like firestore and firebase database, i'm using a system to know where the function failed and to handle error accordingly, simplified version below:
exports.doStuff = functions.https.onCall((data, context) => {
return [promise doing stuff goes here].catch(error => { throw new Error('ERROR0') })
.then(result => {
return [promise doing stuff goes here, needs result of previous promise]
.catch(error => { throw new Error('ERROR1') })
})
.then(result => {
return [promise doing stuff goes here, needs result of previous promise]
.catch(error => { throw new Error('ERROR2') })
})
.then(result => {
//inform client function successful
return {
success: true
}
})
.catch(error => {
if (error !== null) {
switch (error.message) {
case 'ERROR0':
//do stuff
throw new functions.https.HttpsError('unknown', 'ERROR0');
case 'ERROR1':
//do stuff
throw new functions.https.HttpsError('unknown', 'ERROR1');
case 'ERROR2':
//do stuff
throw new functions.https.HttpsError('unknown', 'ERROR2');
default:
console.error('uncaught error: ', error);
throw error;
}
}
});
});
the thing is, for each .catch() inside each returned promise, i'm getting the following warning: warning Avoid nesting promises
so my question is, is there a better way to handle errors?
Ultimately it's a style recommendation to prevent bizarre and hard to recognise errors. Most of the time a rewrite can eliminate the warning. As an example, you could rewrite your code as the following whilst retaining the same functionality.
exports.doStuff = functions.https.onCall(async (data, context) => {
const result1 = await [promise doing stuff goes here]
.catch(error => {
throw new functions.https.HttpsError('unknown', 'ERROR0', { message: error.message } )
});
const result2 = await [promise based on result1 goes here]
.catch(error => {
throw new functions.https.HttpsError('unknown', 'ERROR1', { message: error.message } )
});
const result3 = await [promise based on result1/result2 goes here]
.catch(error => {
throw new functions.https.HttpsError('unknown', 'ERROR2', { message: error.message } )
});
return {
success: true
};
});
Lastly, rather than using unknown everywhere, you could use one of several possible values for the first argument whilst passing in whatever supporting information you need as the third argument (as shown above where I pass through the original error message).
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 3 years ago.
I have a chain of promises in my backend and i need to access the result of the first promise in the second one
mongo.connect()
.then((client) => {
return circolari.infiniteScroll(client, currCirc)
})
.then(({ data, client }) => {
mongo.close(client)
res.send(data)
})
.catch(error => res.sendStatus(error.message))
I need to access client to close the connection in the second promise.
Right now to achieve that I resolve in circolari.infiniteScroll(client, currCirc) an object like this:
resolve({
data: data,
client: client
})
With this workaround it works, but I think there's a better way to do it, thank you.
You can make it a little bit shorter:
mongo.connect()
.then(client => circolari.infiniteScroll(client, currCirc)))
.then(({ data, client }) => {
mongo.close(client);
res.send(data);
})
Or using await and async:
async function returnResponse(res) {
let client;
try {
client = await mongo.connect();
const data = await circolari.infiniteScroll(client, currCirc);
res.send(data);
} catch (err){
res.sendStatus(err.message)
} finally {
await mongo.close(client); // close connection in every case
}
}
foo();
I am using fetch method to get some data from the server. Once I get that data, I need to store some of it (access_token to be more precise cause I am using oauth) in AsyncStorage. I tried doing just AsyncStorage.setItem without await, not how it is shown in https://facebook.github.io/react-native/docs/asyncstorage, and it worked just fine.
I changed it to:
fetch ('site/login', POST ...)
.then((response) => response.json())
.then(async(responseJson) => {
if (user.valid)
await AsyncStorage.setItem('access_token', responseJson.token);
And it works fine too. But I have 2 questions now:
Is my implementation with fetch and async correct?
And what might happen if I don't use await/async in this case?
Sorry, I am kinda new to Promises and Asynchronous methods in Javascript. Thanks!
async/await is just syntactic sugar over Promises. You're already using Promises, so there's no need to do that. Just return the Promise:
fetch ('site/login', POST ...)
.then((response) => response.json())
.then((responseJson) => {
if (user.valid) { // not sure where 'user' came from, but whatever
return AsyncStorage.setItem('access_token', responseJson.token);
} else {
throw new Error('Invalid user');
}
})
.then(_ => { // storage set, don't care about return value
// do stuff
})
.catch((err) => {
// handle error, including invalid user
});
Response to question in comments
The above in async/await would look like this:
async function foo() {
try {
const response = await fetch('site/login', POST ...);
const responseJson = await response.json();
if (user.valid) {
return await AsyncStorage.setItem('access_token', responseJson.token);
} else {
throw new Error('Invalid user');
}
} catch (error) {
// deal with errors
}
}
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
});
This question already has answers here:
why settimeout not delay the function execution?
(2 answers)
Closed 5 years ago.
I'm having trouble debugging my first Javascript tool which uses promises. I feel like I am using the .catch() method correctly, as it matches up with other StackOverflow answers to similar questions that have been asked, however I'd still receiving an UnhandledPromiseRejectionWarning for uncaught promises.
My program fetches a list of objects from an S3 and then logs them to console.
Here is the promise chain
s3Helper.setCredentials(program.profile)
.then(s3Helper.findObjects([], null))
.then(data => console.log(data))
.catch(err => utl.error(err));
And here are the two promises
function findObjects (keyArray, token) {
return new Promise((resolve, reject) => {
var S3 = new AWS.S3({apiVersion: '2006-03-01'});
var params = {
Bucket: program.bucket,
Prefix: program.prefix,
Delimiter: program.recursive ? '' : '/',
ContinuationToken: token
};
S3.listObjectsV2(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
function setCredentials (profile) {
// Sets AWS credentials, and rejects if the profile is not found
return new Promise((resolve, reject) => {
AWS.config.credentials = new AWS.SharedIniFileCredentials({profile: profile});
AWS.config.credentials.refresh((err, data) => {
if (err) {
reject(err);
} else if (AWS.config.credentials.accessKeyId) {
resolve();
} else if (AWS.config.credentials.roleArn) {
resolve();
} else {
var error = {
message: `Given profile '${program.profile}' does not exist`
};
reject(error);
}
});
});
}
Sorry if my style or code is bad, I'm still getting used to Javascript!
Your promise chain should look like this:
s3Helper.setCredentials(program.profile)
.then(() => s3Helper.findObjects([], null))
.then(data => console.log(data))
.catch(err => util.error(err));
Note the function: () => ... on the second line
Then .then() method takes a function as it's argument. So you must pass it a function.
Therefore, .then(s3Helper.findObjects([], null)) would only work if s3Helper.findObjects([], null) returns a function. But based on you definition of s3Helper.findObjects([], null), it doesn't. So you need to update your .then() method to .then(() => s3Helper.findObjects([], null)).