📜 ⬆️ ⬇️

Arrow hell, or a new circle of old problems

Origins


Once upon a time, JavaScript developers were very fond of using anonymous functions (in fact, even now, many people like to use them), and everything would be fine, and the code becomes shorter, and there is no need to invent another name for the function, but sooner or later it turns into “waterfall of functions ”Read that you can only in a good IDE and then a little nagging head.



Here is one example:


Example 1:


$http.request('GET', '/api').then(function (data) { return data.arr.map(function (item) { item.props.filter(function (prop) { return !!prop; }).map(function (prop) { prop.a = prop.a * 2; return prop; }) }).filter(function (item) { return item.props.some(function (prop) { return prop.a > 10; }); }); }, function (error) { throw new TypeError(error); }); 

This is a very simple example, in my practice I met “monsters” for 100 - 200, or even 300+ lines of code, which I had to refactor before understanding what kind of magic they do.


At one time, the transition to the “functional design pattern”, in which each function is declared separately and only references to them are used, made the code beautiful, neat and most importantly easy to read, which is very important when you work with it not one.


This example, for example, acquires the following form:


Example 2:


 $http.request('GET', '/api').then(requestSuccessHandler, requestErrorHandler); function requestSuccessHandler(result) { return result.arr .map(transformResultArr) .filter(filterResultArr); function transformResultArr(item) { item.props .filter(checkProperty) .map(transformProperty) } function filterResultArr(item) { return item.props.some(filterProperty); } function checkProperty(prop) { return !!prop; } function transformProperty(prop) { prop.a = prop.a * 2; return prop; } function filterProperty(prop) { return prop.a > 10; } } function requestErrorHandler(error) { throw new TypeError(error); } 

Although the code is bigger, it is easier to work with this option, it is easier to read and debug.


But all the good did not last long.


Arrow functions


Before we turn to the problem, let's see what the arrow functions are and what they gave us with their arrival.


The switch function is a short record of anonymous functions. In principle, this is all, but in practice it turns out to be very useful. For example, if you need to select from the array all the elements having an id field. Previously, it would be written as:


Example 3:


 arr.filter(function(item) { return !!item.id; }); 

Or so:


Example 4:


 arr.filter(filterID); function filterID(item) { return !!item.id; } 

But with switch functions, this entry will become indecently short:


Example 5:


 arr.filter(item => !!item.id); 

It seems to be all great. And yes, here I agree that the switch functions made the code much more concise, without harming its readability, and in all simple cases it will be so. And I already thought that I found a new staylguide full of anonymous functions, but it was not there.


The problem does not appear immediately.


Arrows around


Recently, I switched to a new project, in which ECMAScript 2015 has been actively used from the very beginning, and as usual, new language features are used to the maximum. The most noticeable of these is the widespread use of switch functions. And everything would be fine if it were not the waterfalls of functions on 100+ lines and 4+ levels of nesting.


For clarity, let's return to the first example, in this project it would look like this:


Example 5:


 $http.request('GET', '/api').then(data => { return data.arr.map(item => { item.props.filter(prop => !!prop) .map(prop => { prop.a = prop.a * 2; return prop; }) }).filter(item => item.props.some(prop => prop.a > 10)); }, error => { throw new TypeError(error) }); 

In general, it became shorter (and by the way, I myself love it when the code is short). In some places it became clearer. But this is only if it is compared with the first example. In practice, such code grows into huge unreadable monsters, which are quickly written, but then spend hours debugging and refining.


But I'm not saying that using arrows is bad, they just need to be used in moderation.


How to do everything better?


On the way to creating good practices with ECMAScript 2015, we will stumble and fall for a long time, but first let us begin to combine the best old and new practices.


For example, connect examples 2 and 5:


Example 6:


 $http.request('GET', '/api').then(requestSuccessHandler, requestErrorHandler); function requestSuccessHandler(result) { return result.arr .map(transformResultArr) .filter(filterResultArr); function transformResultArr(item) { item.props .filter(prop => !!prop) .map(transformProperty) } function filterResultArr(item) { return item.props.some(prop => prop.a > 10); } function transformProperty(prop) { prop.a = prop.a * 2; return prop; } } function requestErrorHandler(error) { throw new TypeError(error); } 

If you use the arrow functions for simple one line functions, you can avoid creating a huge number of small functions without harming the readability of the code, but if the function is more complicated than one action, then it is often better to take it out separately, so that it would be easier if necessary. the developer would easily understand what is happening in this section of the code.


')

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


All Articles