Language: JavaScript
Recursion - Not my Favorite Topic.
Promises - They can get confusing.
Recursion + Promises - I need to program in a padded room.
I made this little JS Fiddle puzzle I call The RecursiveFunHouse as comedic way of keeping my sanity by simplifying the issue into something silly. Hopefully yall can get a laugh out of my pain :)
The Problem:
Each recursive call is dependent on the outcome of the previous call, but in order to get the outcome I must run an asynchronous task and use that outcome in other sub tasks.
The "Recursive Fun House" helped me boil down the issue to this - the original recursive loop is continuing on with an undefined value as the sub tasks are still executing.
The Fun House - loops around collecting random numbers between (-99) and 99. Once the last difference between the last number and the new number is positive the fun is over
Printing "Made it here 1...Made it here 6" should indicate that the sub-tasks were handled correctly and we have a value for the next loop.
Current it prints 1,2,3,6,4,5 :(
recursiveFunHouse.js
var recursiveFunHouse = function(num){
console.log("Made it here 1");
var newNum = performSideTasks();
console.log("Made it here 6");
console.log("newNum");
console.log(newNum);
if(newNum-num >0 ){
recursiveFunHouse(newNum);
}
else{
console.log("The FunHouse Generated These Numbers :")
for(var i = 0; i <numList.length; i++){
console.log(numList[i]);
}
}
};
var performSideTasks = function(){
console.log("Made it here 2");
someAsyncTask().then(function(num){
anotherTask(num);
console.log("made it here 5");
return num;
});
}
var someAsyncTask = function(){
return new Promise (function(resolve, reject) {
console.log("made it here 3");
var randNum = Math.floor(Math.random()*99) + 1;
randNum *= Math.floor(Math.random()*2) == 1 ? 1 : -1;
setTimeout(function() {
numList.push(randNum)
resolve(randNum)
}, 100);
});
}
var anotherTask = function(num){
console.log("made it here 4");
console.log(num);
};
var numList= [];
recursiveFunHouse(20);
Note - Forgive my pathetic return statement return num;
It just shows what I wish I could tell my computer.
Questions About Recursion and Promises:
1) Should I be worried in the first place about going into the next recursive loop with a promise not yet resolved?
2) If not, what is a clean way to keep this function decomposition and force each recursive loop to wait for the resolution of the last call?
Recursion and Promises can seem wizard-level-95 magical sometimes... that's all I'm saying. Question-Done.
When working with a recursive method that returns a promise, the most important thing to remember is that each time the method is called, they create a new promise. All of those promises need to be resolved in order for the original call to be resolved.
Recursion is a process of calling itself. A function that calls itself is called a recursive function. The syntax for recursive function is: function recurse() { // function code recurse(); // function code } recurse();
Anonymous recursion primarily consists of calling "the current function", which results in direct recursion. Anonymous indirect recursion is possible, such as by calling "the caller (the previous function)", or, more rarely, by going further up the call stack, and this can be chained to produce mutual recursion.
Call Back function - Its same sa that of recursive funcion. t may be an infinte loop function also. Recursion means calling itself again and again.
In classical synchronous recursion, recursion state is stored in stack frames pushed and popped off a common execution stack. In asynchronous recursion, recursion state can be stored in Promise objects pushed and popped off the head of a common promise chain. For example:
function asyncThing( asyncParam) { // example operation
const promiseDelay = (data,msec) => new Promise(res => setTimeout(res,msec,data));
return promiseDelay( asyncParam, 1000); //resolve with argument in 1 second.
}
function recFun( num) { // example "recursive" asynchronous function
// do whatever synchronous stuff that recFun does when called
// ...
// and decide what to do with async result: recurse or finish?
function decide( asyncResult) {
// process asyncResult here as needed:
console.log("asyncResult: " + asyncResult);
if( asyncResult == 0)
console.log("ignition");
// check if further recursion is needed:
if( asyncResult < 0)
return "lift off"; // no, all done, return a non-promise result
return recFun( num-1); // yes, call recFun again which returns a promise
}
// Return a promise resolved by doing something async and deciding what to do.
// to be clear the returned promise is the one returned from the .then call
return asyncThing(num).then(decide);
}
// call the recursive function
recFun( 5)
.then( function(result) {console.log("done, result = " + result); })
.catch( function(err) {console.log("oops:" + err);});
Run the code to see its effect.
Core principles (magic) on which this example relies:
then
registration of listener functions returns a pending promise. If a listener is called and returns from execution, listener return value resolves the pending promise. If the listener throws an error instead of returning, the pending promise is rejected with the thrown value.If and when listener functions are executed (because a promise becomes fulfilled or rejected) they are executed asynchronously, in their own call out from the event loop, after code which resulted in the listener being executed has itself run to completion.
This means all log entries made when registering promise listeners (by calling then
on a promise) appear before any log entry made by a registered listener function when executed asynchronously at a later time. Promises do not involve time travel.
Will this stop your head hurting? Perhaps not, but at least it's true.
`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With