📜 ⬆️ ⬇️

Async / Await in javascript. View from the outside



Recently, more and more of my friends, colleagues, and people from the community are talking about working with asynchronous functions, and in particular about using async / await on their projects. I decided to figure out for myself what kind of animal it is and whether it should be used in the development of combat projects.

The first thing I want to dispel is the common misconception that async / await is an ES7 feature.
')
In my opinion, the use of the terms ES6 and ES7 is not very true in itself and can mislead the developers. After the successful release of the ES2015 specification, called ES6, many people had the erroneous view that everything was not included in it and filled through the babel - these are features of ES7. This is not true. Here is a list of what appears with the release of the ES2016 specification. As you can see, it is not so big and async / await doesn’t appear in it.

I want us to talk right. And speaking of this or that feature, they referred to a specific specification within the framework of which it was described and implemented, and not the mythical ES6, ES7 ... ESN .

Moving on. So what is async / await in simple terms?


In open source, async / await is Promise .

When you declare a function as asynchronous, through the magic word async , you say that this function returns a Promise. Every thing you expect inside this function, using the magic word await , the same returns Promise.

This is a very important point for understanding how such functions work and what to expect when working with them.

Let's see what our unicorn looks like and see how it works.


Here is a simple example of an asynchronous Redux action for leaving the office:

export function logout(router) { return async (dispatch) => { try { const {data: {success, message}} = await axios.get('/logout'); (success) ? dispatch({ type: LOGOUT_SUCCESS }) : dispatch({ type: LOGOUT_FAILURE, message }); } catch (e) { dispatch({ type: LOGOUT_FAILURE, e.data.message }); } }; } 

And now we go from the general to the particular


After reading a number of articles and playing on my own, I made up for myself a short brief answering basic questions with a few examples.

What you need to do to get started?


If you do not use any build system, it is enough to install babel and babel-runtime.

 babel test.js -o test-compile.js —optional runtime —experimental 

In other cases, it is better to watch the settings based on their build system and babel version. This is very important, as the settings in the babel5 and babel6 versions are very different.

How is an asynchronous function created?


 async function unicorn() { let rainbow = await getRainbow(); return rainbow.data.colors } 

Creating an asynchronous function consists of two main parts:

1. Use the word async before declaring a function.

As we can see from the example c logout (), this also works when using switch functions. It also works for class functions and static functions. In the latter case, async is written after static.

2. In the body of the function itself, we must use the word await.

Using the word await signals that the main code would wait and not return the response until some action is performed. It simply processes the Promise for us and waits until it returns the resolve or reject. Thus, it seems that the code runs synchronously.

* To work with await, the function must be asynchronous and declared using the async keyword. Otherwise it just won't work.

How does await work and what does it do?


As mentioned earlier, await waits for any Promise. Drawing an analogy with the operation of the Promise object, it can be said that await performs the exact same function as its .then () method. The only significant difference is that it does not require any callback functions for receiving and processing the result. Actually due to this, it seems that the code is executed synchronously.

Well, if await is an analog of .then () in Promise, how can I catch and handle exceptions?


 async function unicorn() { try { let rainbow = await getRainbow(); return rainbow.data.colors; } catch(e) { return { message: e.data.message, somaText: '     ' } } } 

Since the code is in synchronous style, for this reason we can use the good old try / catch to solve such problems.

Additionally, I want to focus on this attention.



Using try / catch is the only way to catch and handle an error. If for some reason you decide not to use it or simply forget it, this can lead to a lack of processing capability, as well as loss at all.

At what point does the following await code execute?


 async function unicorn() { let _colors = []; let rainbow = await getRainbow(); if(rainbow.data.colors.length) { _colors = rainbow.colors.map((color) => color.toUpperCase()); } return _colors; } 

The code following await continues its execution only when the function used with await returns resolve or reject .

What if the function used with await does not return Promise?


If the function used with await does not return Promise, and we already know that await is waiting for it, then the code will continue as if we did not use await at all.

What if I declare an asynchronous function, but do not use await?


 async function unicorn() { let rainbow = getRainbow(); return rainbow; } 

In this case, at the output, we get a simple reference to the Promise function getRainbow ().

What happens if I write several functions using await in a row?


 async function unicorn() { let rainbow = await getRainbow(); let food = await getFood(); return {rainbow, food} } 

This code will be executed sequentially. First, getRainbow () will work, after it returns resolve or reject, getFood () will start working. One challenge, one result.

And if I need to simultaneously receive the result from several calls?


 async function unicorn() { let [rainbow, food] = await Promise.all([getRainbow(), getFood()]); return {rainbow, food} } 

Since we have already figured out that we are dealing with Promise. Therefore, you can use the method. all () of the Promise object for solving such problems.

Additionally, I want to note that the await * arrayOfPromises construction is no longer relevant and has been removed from the specification. When you try to use it, you will receive a message with love that it is better to use Promise.all ().

Sample message:
  await* has been removed from the async functions proposal. Use Promise.all() 

Updated await design information *. Thanks xGromMx and degorov .

What else would be good to know for successful work?


 async function getAllUnicorns(names) { return await Promise.all(names.map(async function(name) { var unicorn = await getUnicorn(name); return unicorn; })); } 

You need to remember that if you start using async / await in your project, you need to be prepared for the fact that almost your entire stack will have to be asynchronous. And this can add quite a few problems and inconveniences.

It seems to be all.

We have dealt with the main theoretical aspects, if something remains unclear or you have something to add, I wait in the comments.

And for me, the time has come to put all the information together, and decide what to do with our asynchronous unicorn. Take with you on a release journey or leave home for your Pet projects.

Findings:


At first glance, asynchronous functions cause positive emotions. Such a nice thing that makes your code synchronously similar, which makes it more concise and readable.

But it is at first glance.

Yes, the code is concise and readable. Yes, it looks pretty. But as soon as you try to write something harder than a normal API or complex interrelated scripts, for example, a queue of tasks for working with a database, you immediately encounter the problem of an asynchronous stack.

Almost all forever green browsers, out of the box, are 93% -98% supported by ES2015 features ( table ). For me, this means that starting a new project, based on the requirements and the stack, I will think about the need for babel on the project.

But, if I decide to use async / await, I will have to use babel. And I can not say that it will add beauty to my code . After all, officially there is no async / await, and it is not known whether it will be at all. And this is a big minus for me.

I also don’t like the fact that if I forgot to use await or simply not successful copy-paste, instead of automatically sending an error, I won’t get anything except for the reference to Promise. This can be fraught with consequences, especially when a large project with several developers.

And the last.

Most tasks using async / await are perfectly solved with the help of generators .

First, they have better support.
Secondly, the work of generators will be more natural and predictable.
Thirdly, babel himself leads such a code to the generator with special settings example 1 , example 2 .

Support in NodeJS


Async / await already experimentally got into V8. This means that from the version of nodejs 7, you can play with it and work right out of the box.
How to do it:
 NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly nvm install 7 nvm use 7 node --harmony-async-await app.js 


Total


I answer the question asked at the very beginning:
If I use asynchronous functions, it is only on my Pet projects and not very large workers, mainly for writing the API. At least until everything is standardized and will not be flagged experimentally.

For example, I liked using them in actions for Redux. Everything looks beautiful and harmonious.

I wrote this material first of all for myself in order to deal with the question that interests me. If this material is still useful to someone, I will be very happy.

Also, in the next article, I would like to compare in detail the different approaches to the implementation of asynchrony (callbacks, promises, generators, atoms). So that this is understandable not only to the gurus, but also to people just starting out in javascript.

Thank you all for your attention. Good luck!

Source: https://habr.com/ru/post/282477/


All Articles