The definition of recursion says that it is a direct or indirect function call from itself.

T. o. The go function in the following example is recursive:

 function pass(f, i) { return f(i); } function go(i) { if (i < 3) { console.log(i); pass(go, i + 1); } else { console.log(new Error().stack); } } go(0); 
 html .as-console-wrapper { top: 0; max-height: none; } 

Now let's replace the pass function with an asynchronous operation:

 function go(i) { if (i < 3) { console.log(i); setTimeout(go, 0, i + 1); } else { console.log(new Error().stack); } } go(0); 
 html .as-console-wrapper { top: 0; max-height: none; } 

What changed? In the first case, the call stack contained a chain go-pass-go-pass-go-pass-go , and in the second there was only one function - go . An asynchronous operation is placed in the operation queue and, when called, is not nested in what it planned.

Is it true to call such a call recursive?

I really want to say no, but I remember tail recursion, which also turns into a loop by the compiler. It seems like the nesting of the call stack for recursion is optional. Another point is that the calling function is not in the process of execution at the moment of the execution of the nested call, but even here the tail recursion calls into question the necessity of this fact ...

  • 2
    In my opinion, the second call is not pure recursion. In your definition of recursion, it says "call out of yourself." And the function is only planning a call. There is no challenge. But, explaining the code to another developer, you can say "this is something like a recursion, but handles." - KoVadim
  • one
    Well, there are at least two fundamental differences: 1) Technically, this is not done as recursion; 2) In this way, you cannot get the result of a function call (if it were), and in the usual recursion, it is possible - andreymal
  • @andreymal, and about the return value is in the definition? And also, what about async / await? I just did not drag them into question, so as not to over-complicate examples :) - Qwertiy ♦
  • 2
    I think that this is not pure recursion, as there is no return to the calling function. If it is called in a separate thread, it waits for the result and uses it - then, perhaps, this can be called recursion. Of course, there are subtle cases here - such as the accumulation of results in a global variable ... - Harry
  • And everyone is afraid to write an answer: D - Qwertiy ♦

2 answers 2

Typically, this behavior is called asynchronous recursion. Isomorphism exists between synchronous and asynchronous code, and asynchronous recursion is isomorphic to ordinary recursion.

Isomorphism, by the way, is quite simple, and “on the fingers” the easiest way to explain it is by the example of promises. If there is a code of the form

 function foo() { // stage1 var promise = stage2async(); stage3sync(); return promise; } 

then he will lead him to this form:

 function foo() { // stage1 var promise = stage3async(); stage2sync(); return promise; } 

Of course, it should be applied to all library functions, including the base API, i.e. it is more a theoretical fabrication, not a practical possibility. As can be seen, this isomorphism is also the opposite to itself.

The existence of such isomorphism, among other things, means that any problems of ordinary recursion can emerge in the asynchronous version. Another thing is that asynchronous solutions are usually less susceptible to attendant problems due to the fact that asynchronous flow is "cheaper" than the system one.

For example, stack overflow .

Regular recursion can easily overflow the stack. Asynchronous recursion is seemingly not subject to this ...

... until to return the value, we do not begin to collect the "sausage" of the callbacks. Or until the platform starts, for diagnostic purposes, to track the asynchronous call stack, which can lead to a memory leak.

By the way, Google Chrome in its implementation of Promise asynchronous call stack tracks - and it is even very convenient. Until a memory leak occurs due to infinite asynchronous recursion.

    And I think that this is not recursion, but unrolling iteration.

    Recursion implies (again imho) a return to the calling context. If we also immediately return from it, we get tail recursion, which can be converted by the compiler into a loop.

    • About the "unrolling" can be in more detail? - Qwertiy ♦
    • @Qwertiy, I began to select words, then I realized that I wouldn’t add anything better to en.wikipedia.org/wiki/Loop_unrolling (or maybe they all read it themselves) - avp