Introduction
I am new in the world of javascript promises and I need to understand how they work correctly...
Until know I have been doing this in my code:
const handleRefresh = () => {
setIsRefreshing(true);
fetchUserData()
.then(async () => { <--------- Using then because I return a promise in fetchUserData
await fetchUserPosts(); <------- Using await here
setIsRefreshing(false);
}).catch(err => { <--------- This will catch the error I have thrown in the function fetchUserPosts or inside the then body
// TODO - Show error
setIsRefreshing(false);
console.log(err)
);
};
const fetchUserData = async () => { <------ async function
const { firebase } = props;
const userId = firebase.getCurrentUser().uid;
const documentRef = firebase.getDatabase().collection("users").doc(userId);
// Fetch all the user information
return documentRef <--------- Returning the promise here
.get()
.then((doc) => {
if (doc.exists) {
// Get all user data
const data = doc.data();
console.log(data);
setUserData(data);
}
})
.catch((err) => {
throw err; <----------- Throwing error
});
};
I don't know if I am doing anti patterns... But basically I need to know if this is a good way and if I am doing this correctly.
Questions
Do I have to declare the fetchUserData function as async to return a promise?
Can I use the async await in the then/catch body?
Can I do this?
const handleRefresh = async () => {
setIsRefreshing(true);
await fetchUserData()
.then(async () => { <--------- Using then because I return a promise in fetchUserData
await fetchUserPosts(); <------- Using await here
}).catch(err => { <--------- This will catch the error I have thrown in the function fetchUserPosts or inside the then body
// TODO - Show error
console.log(err)
);
setIsRefreshing(false);
};
I would really appreciate if someone guides me. Thanks.
the words async and await are only syntactic sugar for then and catch.
This:
fetchUserData()
.then(data => return data )
.catch(error => return error)
is equivalent to:
async function getUserData() {
const userData = await fetchUserData()
return userData
}
Here you are returning anything (success or error). If you want to treat the error here, just put a try/catch clause.
async function getUserData() {
try {
return await fetchUserData()
} catch (e) {
return e.message
}
}
Note that you can only use the await clause within an async function.
1.
Function can return Promise without being declared as async as long as you don't await inside it,
2.
You should not use async-await inside then, simply return a Promise and it'll be resolved in the following then,
3.
When using async-await syntax, Promises are awaited in a declarative way as demonstrated below:
const handleRefresh = async () => {
try
{
const a = await getA()
// pass the result to another Promise
const b = await getB(a)
const c = await getC(b)
} catch (error)
{
handleError(error)
}
};
Related
If we have this function
const getData = () => {
foo()
.then(result => {
return result;
})
.catch(error => {
return error;
});
};
Although getData is not a promise itself, but it contains a promise, which is asyncrnous.
So what is the best way to wait for getData to return something. Async / Await doesn't work cause they work with promises.
Thanks.
Currently, getData() doesn't return anything. You need to make it return a Promise, so you can await it or chain .then() to it.
const getData = () => {
return foo() // <-- here
.then(result => {
return result;
})
.catch(error => {
throw error;
});
};
// Now you can do :
getData().then(...)
// or :
const data = await getData();
In this case, you can also omit the curly braces and the explicit return, and make it implicit :
const getData = () => foo()
.then(result => {
return result;
})
.catch(error => {
throw error;
});
Hey but what's that :
.then(result => {
return result;
})
This does nothing. It takes a value and simply returns it without doing anything. You can remove it.
You can now rewrite getData() this way :
const getData = async () => {
try {
return await foo()
} catch (error) {
throw error;
}
}
For that matter, this :
.catch(error => { throw error; });
or this :
catch (error) { throw error; }
are also pretty useless, they just "relay" (bubble up) the error that has to be caught in the calling function.
Now it's obvious getData does pretty much only one thing, it's returning foo(), which is a Promise. It's only a wrapper around a Promise... so it's actually pretty useless.
Bottom line, detData() is useless altogether. foo is a Promise; writing a function that returns a Promise so you can use it like a Promise is just... a Promise with an extra step. Just use foo directly.
let result;
try {
result = await foo();
} catch (error) {
console.log(error);
}
console.log(result);
This will not work because getData is not returning a value. You can add a return statement before foo call and wait for the return value.
const getData = () => {
return foo();
};
getData().then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
To wait for an operation you must return a Promise or use a callback. The code snippet below runs and should illustrate how this works. I implemented a sample foo function that is actually asynchronous (wait for 1 second before returning the data '12345'). I used async/await to illustrate how that can work, but you can equally return the result of foo and use then instead.
const foo = () => {
return new Promise(resolve => {
setTimeout(() => resolve('12345'), 1000);
});
}
const getData = async () => {
const data = await foo();
console.log(`Data is ${data}`);
return data;
};
getData()
.then(() => console.log('complete'))
.catch(err => console.log(`oops: ${err}`));
console.log('this prints first since async operation is still pending');
I've trying to retrieve the data, but I can't return it, can only see it in the console,
it's a simple axios get function but for some reason, I keep getting Promise even after using async/await.
my goal is to save the data to the memory.
any help would really be appreciated
let fetchTodo = async () => {
await axios.get('https://jsonplaceholder.typicode.com/todos/1')
.then(res => console.log(res.data))
.then(res => { return res })
.catch(err => console.log(err))
};
console.log("TEST: ", fetchTodo())
console
Asycn function always returns a promise, to get data from the fetchTodo function you need to create another async function which will await the result returned by fetchTodo(). if you are using react, you can use states and update the state while you are inside the .then chain of the fetchTodo function.
Asycn function always returns a promise. For getting or saving data you need to get it from .then() function. Here you can check the example. Hope so it will help you.
let fetchTodo = async () => {
await axios.get('https://jsonplaceholder.typicode.com/todos/1')
.then(res => console.log(res.data))
.then(res => {
// here you can performance your task, save data, send
// response or anything else
return res
})
.catch(err => console.log(err))
};
fetchTodo()
The async/await syntax means a function will return a Promise.
If you want to return the value, you could do something like this:
let fetchTodo = async () => {
try {
const res = await axios.get("https://jsonplaceholder.typicode.com/todos/1");
return res;
} catch (error) {
console.log(error);
}
};
// For the folowing code to work, it must be placed inside a async function as well
const res = await fetchTodo();
console.log(`Test: ${res.data}`);
// If it's a Top level call, use the folowing code
const res = fetchTodo().then( res => {
const data = res.data;
// The rest of your code goes here.
// ...
// ...
// ...
}).catch( error => {
console.log(error);
});
Some more information about it on: How can I use async/await at the top level?
So I call the following function:
async function sql_func(){
console.log('anothertest')
async () => {
console.log('third test')
try {
await sql.connect('heres the connection data')
const result = await sql.query`heres a query`
console.log(result)
} catch(err) {
console.log(err)
}
}
}
the first console log anothertest gets logged but the part inside the async () => {} just gets completly skipped. When debugging i see that it just jumps from the async() => { line straight to the closing bracket }
What am I doing wrong?
async function sql_func() {
console.log('anothertest')
await sql.connect('heres the connection data')
const result = await sql.query`heres a query`
console.log(result)
return result
}
In your example there is no need to define another function: async () => { }. This function is never called. Also async functions handles all promises and rejects when a one of the promises is rejected. The catch you can do at the main function level:
const result = await sql_func().catch(error => // do something)
// Or with try / catch
If you want a different error message (e.g.: hide the real error / stack trace):
async function sql_func() {
console.log('anothertest')
await sql.connect('heres the connection data').catch(error =>
Promise.reject('DB Connection error')
)
const result = await sql.query(`heres a query`).catch(error =>
Promise.reject('Query failed')
)
console.log(result)
return result
}
How can we catch error from an async await function from where it's called?
For example, I have a React component which calls a async-await function imported from another module. When I use Promise.reject("An unknown has occurred"); in that function, so in my React component why can't I get the error in asyncAwaitFunction.catch((e)=>console.log(e))?
I even tried throw "An unknown occured", but it doesn't seem to work.
react component
const handleSubmit = async (e) => {
e.preventDefault();
add(formData, code)
.then(() => router.push("/dashboard/manage"))
.catch((e) => setError(e)); //I want error to be catched here
};
functions.js
export const addUser = async (details, code) => {
const isExist = await isUser(code);
if (!isExist) {
const add = db.batch(); //firebase batch write
add.set(userID(code), details); //Add details to databse
add.commit()
.catch((e)=> {
console.log(e); // error occurs confirmed
Promise.reject("Unknown error occurred"); //this does't get catched in component.
});
} else {
Promise.reject("Already Exists!");
}
};
A rejected Promise (either from a Promise that you constructed that rejected, or from a Promise.reject) will only be caught if:
a .catch is added onto the end of that Promise expression, or
that Promise expression is returned inside an async function or a .then, and the caller of the async function or after the .then callback, there's a .catch
So, you should change to something like:
export const addUser = async (details, code) => {
const isExist = await isUser(code);
if (isExist) {
return Promise.reject('Already Exists!');
}
const add = db.batch(); //firebase batch write
add.set(userID(code), details); //Add details to databse
return add.commit().catch((e) => {
console.log(e); // error occurs confirmed
return Promise.reject("Unknown error occurred");
});
};
But do you really need to log in the .commit().catch? If not, it'd be cleaner to just return the commit Promise and catch in the caller:
export const addUser = async (details, code) => {
const isExist = await isUser(code);
if (isExist) {
return Promise.reject('Already Exists!');
}
const add = db.batch(); //firebase batch write
add.set(userID(code), details); //Add details to databse
return add.commit();
};
A Promise that is awaited or returned from an async function will have its errors (or its resolve value) percolate up to the caller of the async function.
i have code like below
const somePromises = values.map(({variable, value}) =>
this.post('/api/values/', {
variable,
value,
item: itemId,
})
);
await Promise.all(somePromises);
if (somecondition) {
params.var = var;
await this.patch('/api/items/${itemId}/', params);
}
the above code works but i want to execute if clause only if there is values has some value or if somePromises is resolved.
so was trying something like below,
const somePromises = values.map(({variable, value}) =>
this.post('/api/values/', {
variable,
value,
item: itemId,
})
);
await Promise.all(somePromises).then (() => {
if (somecondition) {
params.var = var;
await this.patch('/api/items/${itemId}/', params); //but throws err here
}
});
but throws error await cant be used here and async should be used. how can i fix this issue.
could someone help me with this. thanks.
Assuming this.patch returns a Promise, you can return it if it passes the conditions, or return a resolved Promise:
Promise.all(somePromises).then(() => {
if (somecondition) {
return this.patch('/api/items/${itemId}/', params);
} else {
return Promise.resolve(null);
}
}).then(response => {
if (response) {
// do something with response, the returned value from this.patch
}
});
You are using await inside your callback function body. So, that callback function also should by async if you want to await inside it.
await Promise.all(somePromises).then(async () => {
if (somecondition) {
params.var = var;
await this.patch('/api/items/${itemId}/', params);
}
})