-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async/Await misinformation and bad practice #54
Comments
@Svenskunganka thank you for the detailed explanation and your effort improving this document 👍. About this snippet: function getGithubUser(username) {
return new Promise((resolve, reject) => {
fetch(`https://api.github.com/users/${username}`)
.then(response => {
const user = response.json();
resolve(user);
})
.catch(err => reject(err));
})
}
getGithubUser('mbeaudru')
.then(user => console.log(user))
.catch(err => console.log(err)); Yes, the goal here was to have a side-by-side syntax equivalent between promises and async / await. It's quite useless indeed, but not that useless because this new promise returns the jsonified response. But I agree that it could be much better. For the rest, you clearly demonstrated your point and I (or someone with a PR) will adapt the async / await notion accordingly. However, do we need a try / catch if you are awaiting an async function with async / await ? async function getGithubUser(username) { // promise + await keyword usage allowed
const response = await fetch(`https://api.github.com/users/${username}`); // Execution stops here until fetch promise is fulfilled.
const user = response.json();
return user; // equivalent of resolving the getGithubUser promise with user value.
}
async function myFunc() {
try {
const user = await getGithubUser('mbeaudru');
return user;
} catch (err) {
console.log(err);
}
} I would have appreciated a softer tone though, really / super bad is quite condescending, but the quality of the demonstration quite compensates I guess 😅. |
I'm sorry, I did not mean to sound condescending, I apologize for that! Meant it as "bad for your application" because it will be very hard to debug an application that casts all errors into the generic About your first point, the snippet I provided in my original response does too return a JavaScript Object parsed from the JSON response: function getGithubUser(username) {
return fetch(`https://api.github.com/users/${username}`).then(response => response.json());
}
getGithubUser('mbeaudru')
.then(user => {
console.log(typeof user) // Object
})
.catch(err => console.log(err)); This is because a Promise's
Purely depends on application structure I believe. You should async function getUser (id) {
return await Query.prepare("SELECT name from users WHERE id = ?", id)
}
async function routeHandler (req, res) {
try {
let user = await getUser(req.query.id)
if (!user) return res.sendStatus(404) // Not found
return res.status(200).json(user)
} catch (e) {
switch (e.constructor) {
case DatabaseConnectionError:
return res.sendStatus(500) // Internal server error
case QuerySyntaxError:
return res.sendStatus(400) // Bad request
case default:
return res.sendStatus(418) // I'm a teapot
}
}
} I think this is fine for async function getUser (id) {
try {
return await Query.prepare("SELECT name from users WHERE id = ?", id)
} catch (e) {
throw e
}
}
async function routeHandler (req, res) {
try {
let user = await getUser(req.query.id)
if (!user) return res.sendStatus(404) // Not found
return res.status(200).json(user)
} catch (e) {
switch (e.constructor) {
case DatabaseConnectionError:
return res.sendStatus(500) // Internal server error
case QuerySyntaxError:
return res.sendStatus(400) // Bad request
case default:
return res.sendStatus(418) // I'm a teapot
}
}
} |
I should also add that function throws () {
throw new Error("an error")
}
async function doSomething () {
throws()
}
doSomething().catch(e => console.log(e)) // Error: an error Calling a non-async function that throws from an async function wraps the thrown value in a rejected Promise and returns it. |
Could this have to do with dr. Who? |
@mbeaudru I like the changes, however I'd like to point out that what I've lined out in my previous comments holds true for Promises as well, as long as you return them. To illustrate, if we take the updated example: function getUser() => { // This promise will be rejected!
return new Promise((res, rej) => rej("User not found !")
};
function getAvatarByUsername(userId) {
return new Promise((res, rej) =>
getUser(userId)
.then(user => res(user.avatar))
.catch(err => rej(err))
);
}
function getUserAvatar(username) {
return new Promise((res, rej) =>
getAvatarByUsername(username)
.then(avatar => res({ username, avatar }))
.catch(err => rej(err))
);
}
getUserAvatar('mbeaudru')
.then(res => console.log(res))
.catch(err => console.log(err)); // "User not found !" is the same as this: function getUser() { // This promise will be rejected!
return new Promise((res, rej) => rej("User not found !"))
}
function getAvatarByUsername(userId) {
return getUser(userId).then(user => user.avatar)
}
function getUserAvatar(username) {
return getAvatarByUsername(username).then((avatar) => {
return { username, avatar }
})
}
getUserAvatar('mbeaudru')
.then(res => console.log(res))
.catch(err => console.log(err)); // "User not found !" As long as you return the Promise chain, you don't need to catch or return a new Promise. There are also some typos, the first |
I updated the notion accordingly, thanks for your time 👍 ! |
Sorry to be a bother, but function getUser() { // This promise will be rejected!
return new Promise((res, rej) => rej("User not found !"))
};
function getAvatarByUsername(userId) {
return getUser(userId).then(user => res(user.avatar))
}
function getUserAvatar(username) {
return getAvatarByUsername(username).then(avatar => res({ username, avatar }))
}
getUserAvatar('mbeaudru')
.then(res => console.log(res))
.catch(err => console.log(err)); // "User not found !" should be: function getUser() { // This promise will be rejected!
return new Promise((res, rej) => rej("User not found !"))
}
function getAvatarByUsername(userId) {
return getUser(userId).then(user => user.avatar)
}
function getUserAvatar(username) {
return getAvatarByUsername(username).then(avatar => ({ username, avatar }))
}
getUserAvatar('mbeaudru')
.then(res => console.log(res))
.catch(err => console.log(err)); // "User not found !" |
Indeed, I did the change too fast 😅. I don't understand, why don't you submit a PR instead, since you've taken the time to write the fix ? You could be in the contributors list 👍. I wait for your answer before fixing 😉 |
@mbeaudru you did not wait for the answer! |
The Async/Await section spreads some misinformation and bad practices. It explains that
try/catch
must be used whenever anawait
expression is used, but that is not the case. For example, this snippet:is the same as:
You don't need to wrap it in another Promise,
fetch
already returns a promise.This snippet is really bad, which I'll explain:
You don't need to have a
try/catch
block every time you have anawait
expression. You're already catching the error with.catch()
on the last line. Any function thatgetGithubUser
calls that throws, and any function the functionsgetGithubUser()
calls that throws, and so on, will be caught by the last.catch()
. It should be rewritten:Additionally, the
throw new Error(err);
is super bad because you lose the original error type and whatever custom stack trace implementation it has. For example:The error written to console will be
Error: Error: key mistmatch
rather thanTlsHandshakeError: key mismatch
.The above could be:
The code below will catch the error and not crash the application:
Same with the last snippet:
should be:
The text was updated successfully, but these errors were encountered: