Mistakes we make using JavaScript Promises
betamark.com
betamark.com
get("http://data.com/user")
.then(user => get("http://data.com/location" + user.id))
.then(location => createEntry(user, location))
.then(response => {
// handle response
}).catch(err => {
// handle failure
});
Instead, back before async/await made things easier this nested pattern was used (notice the difference in parenthesis location): get("http://data.com/user")
.then(user => get("http://data.com/location" + user.id)
.then(location => createEntry(user, location)))
.then(response => {
// handle response
}).catch(err => {
// handle failure
});
Edit for completeness, this is how it is now with async/await: try {
const user = await get("http://data.com/user");
const location = await get("http://data.com/location" + user.id);
const response = await createEntry(user, location);
// handle response
} catch (error) {
// handle failure
} get('//data.com/user)
.then(user => resolve({user, location: get('//data.com/user')}))
.then(({user, location}) => createEntry(user, location))
.then(response => {
// handle response
}).catch(err => {
// handle failure
});
Async/await is much cleaner now though.I find the async/await form he alludes to at the end to be the only form that improves upon the original callback version.
- Learn how .then()/.catch() works once and it's the same for all libraries.
- Can test against a common API.
- Can combine operations easily like with Promise.all(), which was very difficult with callbacks.
- Chaining is much better since you can return a promise within a promise, which allows for conditional promises.
- No nesting/right shifting, keeping the structure flatter.
// straightforward, no magic
const simpleFunction = function() {
return Promise.resolve({ name: 'simpleObject' });
}
simpleFunction().then(function(response) {
console.log(response); // { name: 'simpleObject' }
});
// alternatively, destructuring the arguments
simpleFunction().then(function({ name }) {
console.log(name); // 'simpleObject'
});> In this example, both promises will be processed asynchronously and only when both of them are resolved, we handle the result.
While that is true if all the promises resolve successfully, if any of the promises get rejected the catch block gets executed as soon as the first rejection occurs. Beware of that behavior, as if you're not aware of it it can leave your system in a bad state (speaking from experience, of course :)
"Ethereum Phishing Detection"
This domain is currently on the MetaMask domain warning list
In my own projects, async/await has made improved readbility and reduced errors.
1) Not calling promises in parallel. Easy to do because it's impossible to run them in parallel with just "await", need to use Promise.all() or something.
2) Forgetting to write "await". If you try to use the return value then now you have a Promise object instead of the actual value. But the worst is when the code doesn't use the return value. Then it has a really subtle race condition that's hard to find. Or if the promise has any errors, then you might get the dreaded "Unhandled promise rejection" with a useless stack trace, and you might need to search the entire codebase to find where it happened.
const xP = getX();
const yP = getY();
const x = await xP;
const y = await yP;
is just as parallel as const [x, y] = Promise.all([getX(), getY()]); const [foo, bar] = await [async1(), async2()] const [foo, bar] = await Promise.all([async1(), async2()])Async/await is pure syntactic sugar and should not reduce errors unless the Promise syntax was implemented incorrectly.
This an implementation issue. Of course you can wrap the non-conformant external code but in this case that means accepting anti-patterns in order to gain the benefit of syntactic sugar. Writing API wrappers that do nothing more than wrap traditional Promise callback code as a rule is a poor substitute for allowing developers to choose the most appropriate implementation. Instead that would favor meaningless promise chaining and unless you ignore a library’s type definitions that is pretty hard to miss. Using async/await isn’t doing anything different in the same sense that using a class is no different than using a function that returns an object. The question is one of readability and flexibility, no one syntax is inherently better that the other because the interpreter does not know the difference.
A function may return a promise or throw an exception. An `async` function may only return a promise. async functions cannot throw exceptions.
``` // This code sucks but you might have to write it if `get` isn't an async function. try { get().catch(_ => /* handle async errors /) } catch { / handle sync errors */ } ```
The async article linked from this article has a toy example for this concept where the result is unused, but most of the time you would be passing the result somewhere. If you were using typescript it could fail at compile time when you try to pass a promise instead of the expected type. Even if you're not, you should notice it never work in your tests.
Still, I think it is easiest to never mess up if you await as soon as you have a promise value. In many common cases this is easy enough.
const userPromise = fetchUser(id)
const itemPromise = fetchItem(itemId)
// Do stuff, maybe even more async stuff
const item = await itemPromise
const user = await userPromise
Since those promises haven't been awaited until later in the code, they could throw and result in an unhandledRejection, which would be pretty bad. Promise.all is much safer since it instantly awaits both promises.But point is that using `async`/`await` as much as possible restricts the programmer to less concurrency, which inevitably means fewer glitches and race conditions. Many people make the mistake of thinking that JavaScript is safe because there's only one thread. But it turns out that most of the hazards of concurrency still exist as long as continuations can interleave with access to shared resources and mutable state.
Does that make more sense? It's late over here, so I might not be super clear.