Given
let doAsynchronousStuff = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(\"abcdefg\"[Math.floor(Math.random()
None of the above. The code in question is not recursive, not quite iterative (though from the point of view of the English language it is iterative, from the point of view of what we normally call iterative in programming it is not, note that from the point of view of the English language recursion is iterative but we don't say that it is in programming), since it is not recursive the phrase "tail-call-optimised" does not apply and it is not non terminating since the function ends with a return.
What it is is a function that schedules a series of functions to execute at a later time one of which is itself.
Scheduling is a design pattern. One of the oldest example of scheduling is process scheduling that operating systems do. One of the next oldest example is cron.
How scheduling works is that the run-time environment (Linux kernel, Windows kernel, the cron process, javascript) saves a "database" (which may be as simple as a linked-list or as high level as SQL or as low-tech as a text file) of some sort of reference to the code that it is supposed to run and conditions that trigger them (check out AWS Lambda service for a very high-level implementation of this idea) and periodically somehow checks to see if the condition is met then execute the code.
For OS kernels the set of conditions includes some sort of fairness algorithm to ensure that all programs get to use the CPU. For cron the condition is the time specification in crontab. For javascript the condition is the event that the callback is registered with (for setTimeout it is the timeout event).
Traditionally, if you were to write your own software for this you'd write it as a simple state machine. The following is C-like pseudocode implementing the same thing as your example above
int tick = 0;
// Assume that there is an API for registering 1ms periodic interrupt
interrupt_1ms periodic () {
tick++;
}
int main (void) {
int timeout = PI + rand(); // a fairly silly way to randomly select 3 or 4 ms
char state = 0;
char result = nul;
char* data = "abcdefg";
while (1) {
if (tick >= timeout && state == 0) {
state = 1;
tick = 0;
timeout = PI + rand();
}
switch (state) {
case 1:
result = data[floor(rand() * 7)];
state = 2;
break;
case 2:
printf("%c", result);
state = 3;
break;
case 3:
state = 0; // reschedule the doAsynchronousStuff
break;
}
}
}
That's sort of the traditional way. What javascript does is not exactly the same but similar in concept. It still uses a forever loop as the core of the event loop but it does not run continuously (that would waste CPU time, heats up the CPU and drain batteries). Instead it blocks calling one of the asynchronous I/O API (select, poll, epoll, kqueue etc. - libuv will select at compile time) and passes control to the OS which would put the process to sleep until one of the registered I/O events is triggered.
Now, notice your code:
let doAsynchronousStuff = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve("abcdefg"[Math.floor(Math.random() * 7)])
}, Math.PI * 1 + Math.random())
})
.then(data => console.log(data))
.then(doAsynchronousStuff)
}
I don't know about you but to me it is significantly easier to reason about than the traditional state machine. OK, for this very simple example the C pseudocode above is quite easy to understand but consider a real-world node.js or jQuery application with tens or hundreds of complex events (in the case of traditional jQuery apps, those events may even unschedule themselves or schedule even more event handlers). As the number of events you have to handle grow what javascript gives you in its syntax becomes far more readable even though for one event a beginner who's not familiar with anonymous functions and asynchronous code may prefer my pseudo-C example.
Even old-school non-promisified callbacks are more readable than the pseudo-C code:
function doAsynchronousStuff () {
setTimeout(function () {
console.log("abcdefg"[Math.floor(Math.random() * 7)]);
doAsynchronousStuff();
}, Math.PI * 1 + Math.random());
}
So the syntax may be new (well, not that new, Lispers have been doing this sort of thing in the 70s) but the idea is old. The core concept may not be recognisable due to the syntax so don't get too distracted by the syntax. It's just scheduling to run something with a timer. And we simply call repeated scheduling "repeated scheduling" (both Google Calendar and Apple Calendar call them "repeat").