Javascript continent and the callback pyramids of hell
I am not the first to write about these wonderful creations of the man. Some call them the Pyramids of doom. Thousands and thousands of small pyramids stand in the Javascript source code of popular web applications. Everyone has made at least two of them and they remain in their place. From time to time bypassing maintainers break their foot on them and rewrite some of pyramids into slightly smaller multiple pyramids. This is how they keep multiplying.
You can tell there's a callback pyramid growing from very simple cues:
xxxxxx(xxxx, function(xxxxxxx) {
xxxxxx(function(xxxxxxx) {
}xxx, ).xxxxxx(xxx, function(xxxxxxx) {
xxxxxx(function(xxxxxxx) {
xxxxxx;
}, xxxxxxx);
});
});
Many nested functions creep their way up, forming a tip, some of the largest sweeping the ceiling limit of 80 columns. Often they divide before they grow very large.
Doom pyramids feed with error handling code. To get what they yearn, they destroy the exception handling mechanism that would lead the errors where it is relevant to solve them. This forces programmers to constantly feed the pyramids with more error handling code, encouraging their growth.
Origins
Origin of doom pyramid come from disruption of linear logic and restrictions on control flow constructs. They cannot thrive in environment where execution can wait for results or fail with ordinary means.
xxxx = xxxxxxx(xxx, xxx, xx);
xxxxx(xxxx, xxx, xx);
xxxx = xxxxxxxxxx(xxx, xxx, xx).xxxx();
xxxx = xxxxxx.xxxx(xxx, xxx, xx);
There is no concept of 'wait for results' in javascript, therefore ordinary programs ongo a violent conversion into continuation passing style as they evolve, turning into the dreaded doom pyramids.
Fear of damaging and uncontrolled concurrency that might occur from waiting makes the javascript a fertile breeding ground for different kinds of doom pyramids.
Subspecies
As programmers try to fix the symptom and not the original problem, various subspecies of doom pyramids have formed. They take a shape as constructs that resemble their own languages.
These subspecies have their own advanced control flow constructs that allows them to live a life of their own. They fight with each others, including the javascript that they protrude from.
Promises
Promises originated as recorded sentences of OOP evangelists. They were eventually rewritten into a complete language of their own that nearly resembles a whole interpreted language of its own.
Internally they contain an interpreter to represent sequential control flow, and inject more promises into promises to represent code that can do calls that wait for subsequent results. The interpreter-likeness makes the promises heavyweight, forming very interesting optimization problems to where they are used.
Lack of understanding that there's an unique language in place result in code that sometimes confusingly resemble doom pyramids themselves
Promises can be told apart by specific keywords they use to represent control flow:
xxxxxx.xxxx(xxxxx, xxxx).then(function(xxx) {
return xxx.xxxx(xxxxxx);
}).then(function(xx) {
var xxxx = xx;
return xxxx.xxx()
}).catch(function(xxxx) {
xxxxxx(xxxx);
});
Promises breed by providing a control flow constructs that can wait for results. Programmers see these shiny things and put them into good use, sometimes. This way the parasites themselves end up dragged long distances.
Async/Await
Async and await is an advanced form of promises that became a true language that is a duplicate of the original with two new keywords and hidden, duplicated control flow constructs. Asyncs and awaits require a transpiler to function, but they slowly asphyxiate the original language from their way.
Async and await spreads by programmer's ignorance, cross-pollinating other languages that didn't have any problems to start with.
To avoid the original fear that concurrent execution would break atomicity constraints in existing programs. The Async and await produce an identical control flow system on top of the language it infects, with the difference that there are keywords 'async' and 'await' sprinkled around according to a rule.
var xxxx = async function() {
var xxx = new XxxxXx();
var xxxx = await xxx.xxx_xxx();
return xxxx;
};
This was from the book of "Javascript's parasites".
Here some other nice resources that may provoke a thought:
- A silly broad compiler question in the stackexchange.com
- Someone conjuring syntax design guidelines from his hat
- Collection of lisp fanboy quotes
Also, as always, there are lots of errors in this blog post. I think it's worthwhile to check into r/programming if you wonder about what's right and what's wrong.