Is there any differences between async/await, and more standard promise notations using .then, .finally, and .catch
for example
functionThatReturnsPromise(someVar).then((returnedValueFromPromise) => {
console.log(returnedValueFromPromise)
})
vs
async functionThatReturnsPromise(someVar) {
await returnedValueFromPromise
}
I'm unsure if Promise chaining is just the old notation, and it's been replaced by the newer async/await. Is there still a use case for both? What is better practice?
You're misunderstanding how it works. With async/await you have a synchronous syntax flow of the code execution that's it:
try {
const returnedValueFromPromise = await functionThatReturnsPromise(someVar);
} catch (err) {
console.error(err);
throw err
}
Notice that
Code block must be inside an async scoped function
You still need to do err handling, can't assume promise will always work good
All values returned by async enclosed function are promises by default
It all depends on what you want. async/await are new. Think of it this way, they allow a promise to run like it was synchronous when it isn't.
Moreover using async/await makes your code cleaner and more readable.
And to answer your question, there are not much differences between them in terms of what they do. The syntax is just quite different.
Promise chaining is not old fashion, they are quite new, just that async/await came a bit later - giving you a "better" way of handling Promises.
Finally, you can use them interchangeably in your code.
Something to be careful with await, is that you'll need to wrap await statements in a try catch block in case the request fails which I personally think is uglier than regular promise chaining.
Related
I have been using ECMAScript 6 and ECMAScript 7 features already (thanks to Babel) in my applications - both mobile and web.
The first step obviously was to ECMAScript 6 levels. I learnt many async patterns, the promises (which are really promising), generators (not sure why the * symbol), etc.
Out of these, promises suited my purpose pretty well. And I have been using them in my applications quite a lot.
Here is an example/pseudocode of how I have implemented a basic promise-
var myPromise = new Promise(
function (resolve,reject) {
var x = MyDataStore(myObj);
resolve(x);
});
myPromise.then(
function (x) {
init(x);
});
As time passed, I came across ECMAScript 7 features, and one of them being ASYNC and AWAIT keywords/functions. These in conjunction do great wonders. I have started to replace some of my promises with async & await. They seem to add great value to programming style.
Again, here is a pseudocode of how my async, await function looks like-
async function myAsyncFunction (myObj) {
var x = new MyDataStore(myObj);
return await x.init();
}
var returnVal = await myAsyncFunction(obj);
Keeping the syntax errors (if any) aside, both of them do the exact same thing is what I feel. I have almost been able to replace most of my promises with async,awaits.
Why is async,await needed when promises do a similar job?
Does async,await solve a bigger problem? Or was it just a different solution to callback hell?
As I said earlier, I am able to use promises and async,await to solve the same problem. Is there anything specific that async await solved?
Additional notes:
I have been using async,awaits and promises in my React projects and Node.js modules extensively.
React especially have been an early bird and adopted a lot of ECMAScript 6 and ECMAScript 7 features.
Why is async,await needed when Promises does similar job? Does async,await solve a bigger problem?
async/await simply gives you a synchronous feel to asynchronous code. It's a very elegant form of syntactical sugar.
For simple queries and data manipulation, Promises can be simple, but if you run into scenarios where there's complex data manipulation and whatnot involved, it's easier to understand what's going on if the code simply looks as though it's synchronous (to put it another way, syntax in and of itself is a form of "incidental complexity" that async/await can get around).
If you're interested to know, you can use a library like co (alongside generators) to give the same sort of feel. Things like this have been developed to solve the problem that async/await ultimately solves (natively).
Async/Await provide a much nicer syntax in more complex scenarios. In particular, anything dealing with loops or certain other constructs like try/catch.
For example:
while (!value) {
const intermediate = await operation1();
value = await operation2(intermediate);
}
This example would be considerably more convoluted just using Promises.
Why is async,await needed when Promises does
similar job? Does async,await solve a bigger problem? or was it just a
different solution to callback hell? As I said earlier, I am able to
use Promises and Async,Await to solve the same problem. Is there
anything specific that Async Await solved?
The first things you have to understand that async/await syntax is just syntactic sugar which is meant to augment promises. In fact the return value of an async function is a promise. async/await syntax gives us the possibility of writing asynchronous in a synchronous manner. Here is an example:
Promise chaining:
function logFetch(url) {
return fetch(url)
.then(response => response.text())
.then(text => {
console.log(text);
}).catch(err => {
console.error('fetch failed', err);
});
}
Async function:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
}
catch (err) {
console.log('fetch failed', err);
}
}
In the above example the await waits for the promise (fetch(url)) to be either resolved or rejected. If the promise is resolved the value is stored in the response variable, and if the promise is rejected it would throw an error and thus enter the catch block.
We can already see that using async/await might be more readable than promise chaining. This is especially true when the amount of promises which we are using increases. Both Promise chaining and async/await solve the problem of callback hell and which method you choose is matter of personal preference.
Async/await can help make your code cleaner and more readable in cases where you need complicated control flow. It also produces more debug-friendly code. And makes it possible to handle both synchronous and asynchronous errors with just try/catch.
I recently wrote this post showing the advantages of async/await over promises in some common use cases with code examples: 6 Reasons Why JavaScript Async/Await Blows Promises Away (Tutorial)
await/async are often referred to as syntactic sugar to promises and let us wait for something (e.g. an API call), giving us the illusion that it is synchronous in an actual asynchronous code, which is a great benefit.
The things you want to achieve with async/await is possible with promises (but without the advantages of async/await). Lets take an example with this code:
const makeRequest = () => //promise way
getJSON()
.then(data => {
return data
})
makeRequest();
const makeRequest = async () => { //async await way
const data = await getJSON();
return data;
}
makeRequest()
Why is async/await prefered over promises?
Concise and clean - We don’t have to write .then and create an anonymous function to handle the response, or give a name data to a variable that we don’t need to use. We also avoided nesting our code. async/await is a lot cleaner.
Error handling - Async/await makes it finally possible to handle both synchronous and asynchronous errors with the same try/catch format.
Debugging - A really good advantage when using async/await is that it’s much easier to debug than promises for 2 reasons: 1) you can’t set breakpoints in arrow functions that return expressions (no body). 2) if you set a breakpoint inside a .then block and use debug shortcuts like step-over, the debugger will not move to the following .then because it only “steps” through synchronous code.
Error stacks - The error stack returned from the promises chain gives us no idea of where the error occurred and can be misleading. async/await gives us the error stack from async/await points to the function that contains the error which is a really big advantage.
My code works with the following:
export async function FetchAPI(props){
const url = `www.website.com/${props}`
let res = await fetch(url)
let response = await res.json()
return response
}
But when I try to clean up the code using better practices, I get error for the below code saying it is returning undefined.
export async function FetchAPI(props){
const url = `www.website.com/${props}`
fetch(url)
.then((resp => {
resp.json()
return resp
}))
}
Anyone understand the differences and how I can get the second one to work?
The correct code for the .then() version is this:
export function FetchAPI(props){
const url = `http://www.website.com/${props}`;
return fetch(url).then(resp => {
return resp.json();
});
}
Is the return type using .then different from without it?
This fixed version would generate the same exact returned results as your await version with the same URL. Both would return a promise that would resolve/reject based on the results of the fetch and the JSON conversion of the body.
Note: URLs used in Javascript programming should contain the protocol as shown here.
I wonder why using thenable rather than async/await is better practice.
Async/await is a more modern syntax. When sequencing multiple asynchronous operations or branching based on asynchronous results, it is pretty much always simpler to write, read and debug with async/await than using .then().
When you just have a single asynchronous operation, you can sometimes just return that promise and using async/await doesn't necessarily provide any advantages. It's entirely up to the designer of the code which one to use - either can work just fine when written properly.
There are times when a simple .catch() is cleaner than putting a try/catch around the whole block too for localized error handling. Like many things with coding style, there's no absolute right and wrong, just opinions on what looks like the cleanest code.
Also, if you want your code to run in older Javascript environments that don't support async/await, then you may need to use .then() and .catch() or transpile your code to an older target.
For a related discussion see Async / await vs then which is the best for performance?.
Is it's possible to rewrite next code with try-catch? I have heard that modern JS support it.
{
// GET /someUrl
this.$http.get('/someUrl').then(response => {
// get body data
this.someData = response.body;
}, response => {
// error callback
});
}
Yes, by try catch I assume you are referring to async/await introduced in ES6. It's worth bearing in mind that async/await only works on promise objects and performs exactly the same function but just provides you a more synchronous way to write your code that is (in most situations) easier to read.
Using async await the promise will resolve and return that value and then proceed to the next line of code, any exceptions can be handled in a catch block like so:
const someFunc = async () => {
try {
const response = await this.$http.get('/someUrl');
console.log(response);
} catch(err){
// Error handling
throw new Error(err);
}
}
While many may point you to async/await so you can use the actual try/catch keywords, I think it's more important to understand the code you have and make an informed decision. Most importantly, promises already handle the try/catch for you. Lets dive in.
In this code, you are dealing with promises.
goDoSomething().then(callThisFunctionWithResultsWhenDone);
One of my favorite properties of Promises are that they already handle
try/catch for you.
goDoSomething()
.then(doSomethingThatThrows)
.catch(doSomethingWithYourError)
.then(otherwiseKeepGoing)
Notice I used the .catch instead of passing an error handler in the same line. The inline error handler doesn't do what you may expect so I avoid it. Using .catch() allows you to intelligently handle asynchronous errors by short-circuiting a series of operations or handling errors from a series of steps individually without breaking out of the pipeline.
goDoSomething()
.then(throwError)
.then(thisIsSkipped)
.then(thisToo)
.catch(handleErrorButDontRethrow)
.then(doThisWithOrWithoutError)
.then(finishUp)
I always like to think of this style as a pipeline. Each catch statement catches any errors above it going up to the previous catch.
So, in your case:
$http.get(...)
.catch(handleRequestError)
.then(process)
.catch(handleProcessingError)
This would handle all possible errors but usually to handle the request error you actually would skip the processing step so then:
$http.get(...)
.then(process)
.catch(handleProcessingError)
Likewise, whatever calls this block may just want to skip the next block if the processing failed. Then you are left with:
$http.get(...)
.then(process)
If you find yourself re-throwing then maybe you should remove the catch and let it bubble.
IMO this is much more idiomatic JS code. The style introduced with async/await is a bit more familiar if you learned other languages first but it brings a very imperative feeling with it. In order to have the flexibility to handle each async operation individually your async/await code would need to look like this:
let intermediateStateStorage;
try {
intermediateStateStorage = await goDoSomething();
} catch {
handleThis();
}
try {
intermediateStateStorage = await process(intermediateStateStorage);
} catch {
handleThisOne();
}
This seems verbose compared to:
goDoSomething()
.catch(handleThis)
.then(process)
.catch(handleThisOne)
Neither are bad and I obviously have preference but I thought you'd like to know what you are dealing with so you can make an informed decision.
I am just getting up to speed on Async/Await in Typescript.
I am converting existing code like this:
getImportanceTypes(): Promise<void> {
return this.importanceTypeService.list()
.then(items => {
this.importanceTypes = items;
});
}
to:
async getImportanceTypes(): Promise<void> {
this.importanceTypes = await this.importanceTypeService.list()
}
The question is: Does this really return a promise? It must, because it compiles successfully, but in my mind, I see the code execution suspending on the await until it completes, then continuing.
The reason I ask is that I have about 10 similiar calls to the above (different type tables) and I would like them to execute in parallel with Promise.all.
Yes, async functions return promises. (In JavaScript and TypeScript both.) async/await is "just" syntactic sugar for the creation and consumption of promises (but, you know, really useful sugar).
The way you've declared that return type is indeed the correct way to do it. There's been some dissention on that point, however. :-) There are some who feel that if the function is declared async, you should be able to just specify its resolution type rather than explicitly mentioning the promise. At present, though, you do use Promise<x> rather than just x.
I've been working with Node for about month or so and I wanted to utilize the latest features available, like Async/Await style, but I do not like using the try/catch blocks with it. I have been trying to make some kind of a wrapper class and/or functions to wrap the async/await.
I used the destructuring logic of await-to-js npm module to mitigate the usage of try/catch partially, but I am somewhat confused on how would I use it outside of an async function without using es6 promises, and how could I chain multiple awaits using this approach?
Any help, suggestion or critics, if I am doing something completely wrong, is more than appreciated.
A good option to avoid the use of try/catch block in async/await is to create an higher-order function for error handling:
function catchErrors(fn) {
return function(...args) {
return fn(...args).catch((err) => {
console.error(err);
})
}
}
async function asyncFunc(name, value) {
const a = await ...
const b = await ...
/* ... */
}
// Wrap it in an higher-order function
const wrappedAsyncFunc = catchErrors(asyncFunc);
wrappedAsyncFunc("lala", 4);
You have to choose. Using try, catch block, or use promises. The reason for async/await exist, is for people who do not like promises, and prefer cleaner code.
If you don't want both, you can use
https://nodejs.org/api/process.html#process_event_uncaughtexception
or
https://nodejs.org/api/process.html#process_event_unhandledrejection