Capturing errors with Async/Await - javascript

I have a part of my code that makes several API calls to different endpoints and I want to know if any of those calls fail so I can display an appropriate error message. Right now, if an error happens in one() it will stop all other calls from happening, but that's not what I want; If an error occurs, I want it to populate the errors object and have the program continue on.
async function gatherData() {
let errors = { one: null, two: null, three: null };
const responseOne = await one(errors);
const responseTwo = await two(errors);
const responseThree = await three(errors);
if (!_.isNil(errors.one) || !_.isNil(errors.two) || !_.isNil(errors.three)) {
// an error exists, do something with it
} else {
// data is good, do things with responses
}
}
gatherData();
async function one(errors) {
await axios
.get("https://jsonplaceholder.typicode.com/comment")
.then(res => {
return res;
})
.catch(err => {
errors.one = err;
return err;
});
}
async function two(errors) {
await axios
.get("https://jsonplaceholder.typicode.com/comments")
.then(res => {
return res;
})
.catch(err => {
errors.two = err;
return err;
});
}
async function three(errors) {
await axios
.get("https://jsonplaceholder.typicode.com/comments")
.then(res => {
return res;
})
.catch(err => {
errors.three = err;
return err;
});
}

If you pass the errors to the async functions, so pass the errors object as parameter
const responseOne = await one(errors);
const responseTwo = await two(errors);
const responseThree = await three(errors);

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);
})

Why is this asynchronous function being called twice

I am trying to create a user with email and password using firebase, but when I call the function that creates it, it is called twice and I get an error because I am trying to register the email that is already in use.
I noticed that the console.log('CALLED') is called once, I don't understand why RegisterWithEmail is called twice. My auth flow only creates the userDocument in the confirmation phase, for this reason userSnap.length equals zero in the second call and tries to create again.
How can I call this function once?
FILE: emailconfirm.page.tsx
registerEmail = async data => {
const { setRegStatus, createDoc } = this.props;
console.log('CALLED')
await RegisterWithEmail(data).then(res => {
console.log('Final response ', res)
if(res === 'EMAIL_VERIFIED') {
createDoc()
setRegStatus({ status: 'created', data: res })
}
else if(res === 'SOMETHING_WENT_WRONG'){
setRegStatus({ status: 'error', data: res })
}
}).catch(err => {
console.log('Error ', err)
setRegStatus({ status: 'error', data: err })
})
}
FILE: firebase.utils.tsx
export const RegisterWithEmail = async user => {
console.log("Called Once...");
if(!user) return 'SOMETHING_WENT_WRONG';
else {
const snap = await firestore.collection('users').where('email', '==', user.email).get();
const docs = snap.docs.map((doc) => doc.data());
if (docs.length !== 0) return 'EMAIL_HAS_ALREADY_BEEN_TAKEN';
try {
console.log("Trying to register email...");
return await auth.createUserWithEmailAndPassword(user.email, user.password).then(async usr => {
await usr.user.updateProfile({
displayName: user.name
}) // SETTING NAME
const sendVerifyEmail = usr.user.sendEmailVerification().then(() => setTimer(usr.user, 5))
return await sendVerifyEmail.then(msg => {
console.log('Finishing...', msg)
if(msg.txt !== 'waiting') {
if(msg.error) {
throw msg.txt
}
else return msg.txt
}
}).catch(() => {
throw 'EMAIL_NOT_SENT'
})
}).catch(() => {
throw 'USER_NOT_CREATED'
})
} catch (err) {
throw 'USER_ALREADY_REGISTERED'
}
}
}
Developer console:
You shouldn't be mixing and matching .then()s in async functions for your own sanity's sake.
Something like
export const RegisterWithEmail = async (user) => {
if (!user) return false;
const snap = await firestore.collection("users").where("email", "==", user.email).get();
const docs = snap.docs.map((doc) => doc.data());
if (docs.length !== 0) return false;
console.log("Trying to register email...");
try {
const resp = await auth.createUserWithEmailAndPassword(user.email, user.password);
// then ...
return true;
} catch (err) {
// catch ...
}
};
might work better for you.
I need more code to be sure, but I think you should add await
registerEmail = async data => {
console.log('CALLED')
await RegisterWithEmail(data)
}

parsing data from an api call into a text file using axios

I am parsing data from an API call into a text file. However, I wanted to use async-await and break the call below call into 3 separate functions.
#!/usr/bin/env node
const yargs = require("yargs");
const axios = require("axios");
const fs = require("fs");
const options = yargs
.usage("Usage: -n <name>")
.option("n", {
alias: "name",
describe: "Your name",
type: "string",
demandOption: true,
})
.option("s", { alias: "search", describe: "Search Term", type: "string" })
.argv;
const greetings = `Hello ${options.name}!`;
console.log(greetings);
console.log("Here's a random joke for you: ");
const url = options.search
? `https://icanhazdadjoke.com/search?term${escape(options.search)}`
: " https://icanhazdadjoke.com/";
axios.get(url, { headers: { Accept: "application/json" } }).then((res) => {
if (options.search) {
res.data.results.forEach((j) => {
fs.appendFile("jokes.txt", "\n" + j.jokes, (err) => {});
});
if (res.data.results.length === 0) {
console.log("no joke found 😭");
}
} else {
fs.appendFile("jokes.txt", res.data.joke, (err) => {
if (err) throw err;
console.log("File Updated");
});
}
});
So the above code works absolutely fine and generates the file perfectly, however when I tried to break it into the following below functions, I just get undefined in the text file, I am not sure why this is happening.
const getJoke = async (url) => {
try {
const joke = await axios.get(url, {
headers: { Accept: "application/json" },
});
return joke;
} catch (error) {
console.error(error);
}
};
const parseJokes = (res) => {
if (options.search) {
res.data.results.forEach((j) => {
return `\n ${j.joke}`;
});
if (res.data.results.length === 0) {
console.log("no joke found 😭");
}
} else {
return res.data.joke;
}
};
const addJokeToFile = async () => {
const result = await getJoke(url)
.then((res) => {
parseJokes(res);
})
.catch((err) => {
console.error(`ERROR: ${err}`);
});
fs.appendFile("jokes.txt", result, (err) => {
console.error(err);
});
};
In the second (functional approach) addJokeToFile method, you are waiting for the promise to be resolved using both ways, await and .then, following modification to the code, might help you get through:
const addJokeToFile = async () => {
getJoke(url)
.then((res) => {
// Aside, we should also return some value from parseJokes function for "no joke found 😭" case, or return null and put a check here and only append to file when jokeString is not null.
const jokeString = parseJokes(res);
fs.appendFile("jokes.txt", jokeString, (err) => {
console.error(err);
});
})
.catch((err) => {
console.error(`ERROR: ${err}`);
});
};
Try using appendFile from 'fs/promises' so that you can stick with the async/await style. Since getJoke returns a promise I would expect result to be a Promise<string | undefined> depending on if any errors show up earlier in the chain.
const { appendFile } = require('fs/promises');
const addJokeToFile = async () => {
try {
const result = await getJoke(url);
const parsed = parseJokes(result);
await appendFile('jokes.txt', parsed);
} catch (err) {
console.error(err);
}
};

NodeJs: Await inside mssql 'done' emitter

I am using the 'mssql' package to process a sql query, calling an async function when the 'row' emitter fires.
When the 'done' emitter fires, I want to wait for all tasks to complete... this is where i am having a problem.
EXAMPLE:
const sql = require('mssql')
let tasks = [];
sql.connect(config, err => {
const request = new sql.Request()
request.stream = true;
request.query('select * from theTable')
request.on('row', row => {
task.push( callAsyncFunction(row) );
})
request.on('error', err => {
// Do something
})
request.on('done', result => {
await Promise.all(tasks);
//THIS IS WHERE THE PROBLEM IS:
//SyntaxError: await is only valid in
// async function
})
})
sql.on('error', err => {
// handle error
})
Can anyone help with my syntax here?
Thank you!
try making anonymous function async
you can make anonymous functions async too
request.on('done', async(result) => {
await Promise.all(tasks);
})
You need to annotate the anonymous function with async:
const sql = require('mssql')
let tasks = [];
sql.connect(config, err => {
const request = new sql.Request()
request.stream = true;
request.query('select * from theTable')
request.on('row', row => {
task.push( callAsyncFunction(row) );
})
request.on('error', err => {
// Do something
})
request.on('done', async (result) => {
await Promise.all(tasks);
//THIS IS WHERE THE PROBLEM IS:
//SyntaxError: await is only valid in
// async function
})
})
sql.on('error', err => {
// handle error
})

Javascript - Chain multiple Fetch promises

I have this method who performs 3 window.fetch
const API_URL = 'http://localhost:3000/'
, API = {
'getArtistLyric': artist => {
return fetch(`${API_URL}artist?name=${artist}`)
.then(res => res.json())
.then(res => {
const artistID = JSON.parse(res).message.body.artist_list[0].artist.artist_id;
console.log('Artist ID is:', artistID);
fetch(`${API_URL}artist/albums/?artist_id=${artistID}`)
.then(resp => resp.json())
.then(resp => {
const trackID = JSON.parse(resp).message.body.album_list[0].album.album_id;
console.log('Random Track ID is:', trackID);
fetch(`${API_URL}artist/album/songsnippet?track_id=${trackID}`)
.then(response => response.json())
.then(response => {
const lyricSnippet = JSON.parse(response).message;
console.log('Track Id lyric snippet is:', lyricSnippet);
})
.catch(err => {
console.error(err);
});
})
.catch(err => {
console.error(err);
});
})
.catch(err => {
console.error(err);
});
}
}
Now i want to call it like this
API.getArtistLyric('Prodigy').then(res).catch(err);
What's the best practice here?
If you want to make a chain requests it's better to use async/await :
async func(){
let response = await /* some request */
let res = await /* another request */
...
return results;
}
Here you can use try/catch syntax and wrap specific request :
try {
let response = await...
} catch ( exception) {
...
}
Also you can wrap a couple of requests.
(async() => {
const API_URL = 'http://localhost:3000/';
const API = {
getArtistLyric: async(artist) => {
try {
const res = await fetch(`${API_URL}artist?name=${artist}`);
const artistID = JSON.parse(res.json()).message.body.artist_list[0].artist.artist_id;
console.log('Artist ID is:', artistID);
const resp = await fetch(`${API_URL}artist/albums/?artist_id=${artistID}`);
const trackID = JSON.parse(resp.json()).message.body.album_list[0].album.album_id;
console.log('Random Track ID is:', trackID);
const response = await fetch(`${API_URL}artist/album/songsnippet?track_id=${trackID}`);
const lyricSnippet = JSON.parse(response.json()).message;
console.log('Track Id lyric snippet is:', lyricSnippet);
return lyricSnippet;
} catch (e) {
console.error(e);
}
}
}
try {
const art = await API.getArtistLyric('Prodigy');
console.log(art);
} catch (e ){
console.error(e);
}
})()
Check out this link regarding chaining promises: https://javascript.info/promise-chaining
Here's the core idea:
It looks like this:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1 return result * 2;
}).then(function(result) { // (***)
alert(result); // 2 return result * 2;
}).then(function(result) {
alert(result); // 4 return result * 2;
});
The idea is that the result is passed through the chain of .then
handlers.
Here the flow is:
The initial promise resolves in 1 second (*), Then the .then handler
is called (**). The value that
it returns is passed to the next .then handler (***) …and so on.

Categories

Resources