Recursion in Promises - javascript

I am writing some software where one can execute a list of actions. each of these actions basically is a Promise.
Each action might have a timer Trigger (where the next action is triggered when the timer ran down). Also, the list of actions might be loopable.
Now it might be possible, that a list of actions e.g. consists out of 2 items, each is executed 0 seconds after finishing the last one, and looping is enabled, thus actually generating an infinite loop.
This is no error in the application and thus be possible to do. My Code looks (extremely simplified) like this at the moment:
const actions = [...] // (() => Promise<void>)[]
const current = 0
const loop = true
const autoTrigger = true
const go() {
if(current > actions.length && loop) current = 0
current ++
return Promise.resolve().then(() => {
return actions[current - 1]()
}).then(() => {
if(autoTrigger) return go()
return Promise.resolve()
})
}
Is this code safe to run, assuming that it might not be aborted until it iterated a few hundred thousand times, or might it cause an error because the recursion gets too deep?

Yes, it is safe to run. Because promises don't call their .then() handlers until after the interpreter has returned back to an empty stack (even if they resolve immediately), there is no stack build-up with this type of recursive-like coding style - no matter how many times it recurses.
So, this is safe, regardless of how many iterations it runs. It could even run indefinitely without any stack build-up.

Related

What happens first: setTimeout 0 or await Promise.resolve?

I'm seeing this behavior in Node and Chrome:
setTimeout(()=>{ console.log('timeout') }, 0)
Promise.resolve().then(()=>{ console.log('promise') })
console.log('sync')
// output order:
// sync
// promise
// timeout
My question is, is this consistent behavior? I.e, according to spec, does a then or await on a memoized/already resolved promise always fire before setTimeout(fn, 0)?
I want to use this in something like the following, returning one thing if I have a memoized result in my promise and another if not:
// somewhere during object initialization
this.resultingPromise = expensiveAsyncFunction()
// in a method called frequently
Promise.race([
new Promise(resolve => setTimeout(() => resolve('default'), 0)),
this.resultingPromise
])
Promise.resolve will schedule a microtask while setTimeout schedule a macrotask. And the microtasks will run before running the next macrotask.
More information about event loop in general: https://www.youtube.com/watch?v=8aGhZQkoFbQ
More technical details about events loop: https://www.youtube.com/watch?v=cCOL7MC4Pl0
so you have 2 async waiting States, but notice that one of them is constant and one is changing (variable). The timeout is set in an XML variable aside while the promise could took forever. If I understood your question quite well, when you have something you rely on take too long and something too short, unless you have a constant applied on one of them like the timeout, then one might end up running shorter unexpectedly (!) Be prepared for that and instead use monolithic structure from code security reasons and not performance.
There's no guarantee that one will be before the other. If you want to guarantee the order of execution - use Promises.
From my understanding Promise has a higher priority in the call stack than setTimeout, and of course synchronous code block will be the first to be executed. In that case, yes the observed behaviour above (in the order of synchronous code block, promise.resolve, and setTimeout 0) should be consistent.

Why is this not a memory leak? [duplicate]

In this answer, a promise chain is built recursively.
Simplified slightly, we have :
function foo() {
function doo() {
// always return a promise
if (/* more to do */) {
return doSomethingAsync().then(doo);
} else {
return Promise.resolve();
}
}
return doo(); // returns a promise
}
Presumably this would give rise to a call stack and a promise chain - ie "deep" and "wide".
I would anticipate a memory spike larger than either performing a recursion or building a promise chain alone.
Is this so?
Has anyone considered the memory issues of building a chain in this way?
Would memory consumption differ between promise libs?
a call stack and a promise chain - ie "deep" and "wide".
Actually, no. There is no promise chain here as we know it from doSomeThingAsynchronous.then(doSomethingAsynchronous).then(doSomethingAsynchronous).… (which is what Promise.each or Promise.reduce might do to sequentially execute handlers if it was written this way).
What we are facing here is a resolve chain1 - what happens in the end, when the base case of the recursion is met, is something like Promise.resolve(Promise.resolve(Promise.resolve(…))). This is only "deep", not "wide", if you want to call it that.
I would anticipate a memory spike larger than either performing a recursion or building a promise chain alone.
Not a spike actually. You'd slowly, over time, build a bulk of promises that are resolved with the innermost one, all representing the same result. When, at the end of your task, the condition is fulfilled and the innermost promise resolved with an actual value, all of these promises should be resolved with the same value. That would end up with O(n) cost for walking up the resolve chain (if implemented naively, this might even be done recursively and cause a stack overflow). After that, all the promises except for the outermost can become garbage collected.
In contrast, a promise chain that is built by something like
[…].reduce(function(prev, val) {
// successive execution of fn for all vals in array
return prev.then(() => fn(val));
}, Promise.resolve())
would show a spike, allocating n promise objects at the same time, and then slowly resolve them one by one, garbage-collecting the previous ones until only the settled end promise is alive.
memory
^ resolve promise "then" (tail)
| chain chain recursion
| /| |\
| / | | \
| / | | \
| ___/ |___ ___| \___ ___________
|
+----------------------------------------------> time
Is this so?
Not necessarily. As said above, all the promises in that bulk are in the end resolved with the same value2, so all we would need is to store the outermost and the innermost promise at one time. All intermediate promises may become garbage-collected as soon as possible, and we want to run this recursion in constant space and time.
In fact, this recursive construct is totally necessary for asynchronous loops with a dynamic condition (no fixed number of steps), you cannot really avoid it. In Haskell, where this is used all the time for the IO monad, an optimisation for it is implemented just because of this case. It is very similar to tail call recursion, which is routinely eliminated by compilers.
Has anyone considered the memory issues of building a chain in this way?
Yes. This was discussed at promises/aplus for example, though with no outcome yet.
Many promise libraries do support iteration helpers to avoid the spike of promise then chains, like Bluebird's each and map methods.
My own promise library3,4 does feature resolve chains without introducing memory or runtime overhead. When one promise adopts another (even if still pending), they become indistinguishable, and intermediate promises are no longer referenced anywhere.
Would memory consumption differ between promise libs?
Yes. While this case can be optimised, it seldom is. Specifically, the ES6 spec does require Promises to inspect the value at every resolve call, so collapsing the chain is not possible. The promises in the chain might even be resolved with different values (by constructing an example object that abuses getters, not in real life). The issue was raised on esdiscuss but remains unresolved.
So if you use a leaking implementation, but need asynchronous recursion, then you better switch back to callbacks and use the deferred antipattern to propagate the innermost promise result to a single result promise.
[1]: no official terminology
[2]: well, they are resolved with each other. But we want to resolve them with the same value, we expect that
[3]: undocumented playground, passes aplus. Read the code at your own peril: https://github.com/bergus/F-Promise
[4]: also implemented for Creed in this pull request
Disclaimer: premature optimization is bad, the real way to find out about performance differences is to benchmark your code, and you shouldn't worry about this (I've only had to once and I've used promises for at least 100 projects).
Is this so?
Yes, the promises would have to "remember" what they're following, if you do this for 10000 promises you'd have a 10000 long promise chain, if you don't then you won't (for example, with recursion) - this is true for any queueing flow control.
If you have to keep track of 10000 extra things (the operations) then you need to keep memory for it and that takes time, if that number is a million it might not be viable. This varies among libraries.
Has anyone considered the memory issues of building a chain in this way?
Of course, this is a big issue, and a use case for using something like Promise.each in libraries like bluebird over thenable chaining.
I've personally had in my code to avoid this style for a quick app that traverses all the files in a VM once - but in the vast majority of cases it's a non issue.
Would memory consumption differ between promise libs?
Yes, greatly. For example bluebird 3.0 will not allocate an extra queue if it detects a promise operation is already asynchronous (for example if it starts with a Promise.delay) and will just execute things synchronously (because the async guarantees are already preserved).
This means that what I claimed in my answer to the first question isn't always true (but is true in the regular use case) :) Native promises will never be able to do this unless internal support is provided.
Then again - it's no surprise since promise libraries differ by orders of magnitude from one another.
I just came out a hack that may help solving the problem: don't do recursion in the last then, rather, do it in the last catch, since catch is out of the resolve chain. Using your example, it would be like this:
function foo() {
function doo() {
// always return a promise
if (/* more to do */) {
return doSomethingAsync().then(function(){
throw "next";
}).catch(function(err) {
if (err == "next") doo();
})
} else {
return Promise.resolve();
}
}
return doo(); // returns a promise
}
To complement the awesome existing answers I'd like to illustrate the expression, which is the result of such an asynchronous recursion. For the sake of simplicity I use a simple function that computes the power of a given base and exponent. The recursive and base case are equivalent to those of the OP's example:
const powerp = (base, exp) => exp === 0
? Promise.resolve(1)
: new Promise(res => setTimeout(res, 0, exp)).then(
exp => power(base, exp - 1).then(x => x * base)
);
powerp(2, 8); // Promise {...[[PromiseValue]]: 256}
With the help of some substitution steps the recursive portion can be replaced. Please note that this expression can be evaluated in your browser:
// apply powerp with 2 and 8 and substitute the recursive case:
8 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 8)).then(
res => 7 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 7)).then(
res => 6 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 6)).then(
res => 5 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 5)).then(
res => 4 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 4)).then(
res => 3 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 3)).then(
res => 2 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 2)).then(
res => 1 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 1)).then(
res => Promise.resolve(1)
).then(x => x * 2)
).then(x => x * 2)
).then(x => x * 2)
).then(x => x * 2)
).then(x => x * 2)
).then(x => x * 2)
).then(x => x * 2)
).then(x => x * 2); // Promise {...[[PromiseValue]]: 256}
Interpretation:
With new Promise(res => setTimeout(res, 0, 8)) the executor is invoked immediately and executes a non-bllocking computation (mimicked with setTimeout). Then an unsettled Promise is returned. This is equivalent with doSomethingAsync() of the OP's example.
A resolve callback is associated with this Promise via .then(.... Note: The body of this callback was substituted with the body of powerp.
Point 2) is repeated and a nested then handler structure is build up until the base case of the recursion is reached. The base case returns a Promise resolved with 1.
The nested then handler structure is "unwound" by calling the associated callback correspondingly.
Why is the generated structure nested and not chained? Because the recursive case within the then handlers prevents them from returning a value until the base case is reached.
How can this work without a stack? The associated callbacks form a "chain", which bridges the successive microtasks of the main event loop.
This promise pattern will generate a recursive chain. So, each resolve() will create a new stack frame (with its own data), utilizing some memory. This means that large number of chained functions using this promise pattern can produce stack overflow errors.
To illustrate this, I'll use a tiny promise library called Sequence, which I've written. It relies on recursion to achieve sequential execution for chained functions:
var funcA = function() {
setTimeout(function() {console.log("funcA")}, 2000);
};
var funcB = function() {
setTimeout(function() {console.log("funcB")}, 1000);
};
sequence().chain(funcA).chain(funcB).execute();
Sequence works great for small/medium sized chains, in the range of 0-500 functions. However, at about 600 chains Sequence starts degradating and generating often stack overflow errors.
The bottom line is: currently, recursion-based promise libraries are more suitable for smaller/medium sized function chains, while reduce-based promise implementations are ok for all cases, including larger chains.
This of course doesn't mean that recursion-based promises are bad. We just need to use them with their limitations in mind. Also, it's rare that you'll need to chain that many calls (>=500) via promises. I typically find myself using them for async configurations which utilize heavily ajax. But even if the most complex cases I haven't seen a situation with more than 15 chains.
On a side note...
These statistics were retrieved from tests performed with another of my libraries - provisnr - which captures the achieved number of function calls within a given interval of time.

Avoiding stack overflow by using setTimeout

I've found the following question here:
The following recursive code will cause a stack overflow if the array
list is too large. How can you fix this and still retain the recursive
pattern?
And the answer:
The potential stack overflow can be avoided by modifying the
nextListItem function as follows:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
};
The stack overflow is eliminated because the event loop handles the
recursion, not the call stack. When nextListItem runs, if item is not
null, the timeout function (nextListItem) is pushed to the event queue
and the function exits, thereby leaving the call stack clear. When the
event queue runs its timed-out event, the next item is processed and a
timer is set to again invoke nextListItem. Accordingly, the method is
processed from start to finish without a direct recursive call, so the
call stack remains clear, regardless of the number of iterations.
Can somebody please explain to me:
whether this use case is ever practical
why long array can cause stack overflow
This is just a hacky alternative to trampolines, which in turn are just a hacky alternative to TCO.
When you call a function in Javascript, you add a frame to the call stack. That frame contains information about the variables in the scope of the function and how it was called.
Before we call a function, the call stack is empty.
-------
If we call function foo, then we add a new frame to the top of the stack.
| foo |
-------
When foo finishes executing, we pop the frame off the stack again, leaving it empty again.
Now, if foo in turn calls another function bar, then we'll need to add a new frame onto the stack, whilst foo is executing.
| bar |
| foo |
-------
Hopefully you can see that if a function calls itself recursively it keeps adding new frames to the top of the call stack.
| ... |
| nextListItem |
| nextListItem |
| nextListItem |
| nextListItem |
----------------
Recursive functions will keep adding frames until either they finish processing, or they exceed the max length of the call stack, resulting in an overflow.
Because setTimeout is an asynchronous operation, it doesn't block your function, which means nextListItem will be allowed to finish and its frame can be popped off the call stack—preventing it from growing. The recursive call will be handled with the event loop instead.
Is this pattern ever useful? The max size for the call stack depends on your browser, but it can be as low as 1130. If you wanted to process an array with a few thousand elements using a recursive function, then you'd risk blowing the call stack.
Trampolines use a similar technique, but rather than offloading the work to the event loop, you return a function which calls the next iteration instead, then the calls can be managed with a while loop (which doesn't affect the stack).
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
return nextListItem;
}
};
while(recur = recur()) {}
It normally isn't, but in the off chance you decide you need to recursively chain the same function call for long sequences this could come in handy.
Stack overflow during recursive operations occurs when the amount of stack memory allocated for a particular program has been fully utilized. A sufficiently long array that is traversed recursively can cause a stack overflow. Perhaps you do not understand how the call stack works?
The repeating for loop is the most efficient for the code posted. However, if your actual code cannot be fitted to using a for loop, then there is another alternative.
The use of setTimeout depends on your opinion of 'practical,' so let's just list the facts so you can decide for yourself.
setTimeout forces the browser to swamp the CPU with operations to get millisecond precision timing. This can be very power inefficient.
With setTimeout, every iteration will cost 4ms. Just 4 iterations will take the time for a whole frame to get rendered. 250 iterations, and a whole second goes by.
But there is an alternative to setTimeout that will help you achieve exactly what you are trying to do without using setTimeout: the DeferStackJS library. If you use DeferStackJS, then all you would need to do is as follows.
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
DeferStack( nextListItem );
}
};
Please emphasize that the above snippet of code is intended for demonstrating how to integrate DeferStackJS. Truth be told, using either DeferStackJS or Array.prototype.pop would be very inappropriate for this specific snippet of code. Instead, the following code would beat both of them hands down.
var list = readHugeList();
var nextListItem = function() {
"use strict";
var item, i = list.length;
while (i--) { // Bounds check: as soon as i === -1, the while loop will stop.
// This is because i-- decrements i, returning the previous
// value of i
item = list[i];
if (!item) break; // break as soon as it finds a falsey item
// Place code here to be executed if it found a truthy value:
// process the list item...
}
if (i !== -1) {
// Place code here to be executed if it found a falsey value:
}
};
The only reason DeferStackJS is mentioned is because I am a firm believer in that the #1 duty of a person answering a forum is to answer the original question asked. Then after they answer that they can put up commentary second-guessing the question about what was intended to be asked like this.

Does following code wait for forEach to finish

I have a large array containing various data, and a function that performs some basic but somewhat time-consuming analysis on the array.
I use a forEach loop to iterate over the array. I want the function to return a specific element if a condition is met, otherwise false, using Promises since the function could take a considerable amount of time to complete:
var analyzeData = function (arr) {
return new Promise(function (resolve, reject) {
var answer = false;
arr.forEach(function (elm) {
if (elm.foo == true) {
answer = elm;
}
});
resolve(answer);
});
}
The issue I'm encountering is that I suspect that answer is being resolved before the forEach loop completes; i.e. before the forEach loop can finish checking every element's foo property, the function resolves to answer, which is by default false.
Is there a way to ensure that the code following the forEach loop waits for the loop to finish iterating, or loop through the array in some other manner?
JavaScript loop iteration is synchronous, and in your code you will always complete the loop before the call to resolve is made.
Note that JavaScript is also single threaded, so putting this in a promise won't make it run in the background. There will still be some period of time in which the UI not responsive to the user while this loop is executing.
You can offload some computation to background processes using web workers.
As an aside: can you exit the loop when the first item is found? If not, why not iterate in reverse (using a for-loop, not array reversal) and exit when the first item is found? It is still technically O(N) in the worst case, but the best case is O(1) if you bail out as soon as possible.
However to be more helpful, you will need to provide more information about what is happening. I would suggest putting together a small example on http://jsfiddle.net/ -- that can be a useful exercise in and of itself.

Error traversing large arrays in JavaScript without timeout

When I'm am executing the following code in Node.js, I get the folliowing error:
RangeError: Maximum call stack size exceeded
This is the code:
var arr = [];
for (var i = 0; i <= 1000000; i++)
arr.push(i);
function nextStep(index) {
if (index === arr.length) {
return;
} else {
console.log(index);
}
nextStep(++index);
}
nextStep(0);
I have no clue what is happening, but near index = 17938, the execution terminates.
Using a setTimeout() helps. What could be wrong here?
You are entering a recursive function. This means that the first function won't return until all the other functions have returned. So if you had four items,
fn(item1)
calls -> fn(item2)
calls -> fn(item3)
calls -> fn(item4)
As you can see, the nesting builds up and up. This is called the stack. There is a maximum size for the stack, to prevent infinite recursion and runaway processes. You have found it with 17938.
This is an inherent flaw with recursion. It can be a stylish way to approach a task, but it has its limitations. The best way to correct it is to use a loop instead:
for (var i = 0; i < arr.length; i++) {
Using setTimeout also works, because the function is not called from the function itself, but is executed with a new stack. However, it will have significantly lower performance than a loop or than a normal recursive function.
There's a certain number of calls that can be made, based on the call stack of different browsers. Most likely, you are testing your code in Chrome, since, I believe, it has the call stack near 20.000. Your code will execute the nextStep function more than 20.000 times (1 million), which means that if your function will not return something until it reaches the call stack limit of that particular browser, it will generate the error that you are getting.
Calling nextStep inside nextStep causes a stack overflow since you never return from the function (unless you found the end of the array, and if the array is too big you will never reach it until the stack overflows).
An example:
You are going to move all the stones from one place to another. Your function is like going to the place where the stones are and pick up one to be delivered to another place. But before you can deliver the stone, you must pick up another, and before you can deliver that you need to pick up another... and soon you are carrying 17938 stones. That is a little bit to heavy and you get crushed under all the stones. (or in the javascript case you get an exception)
When you use setTimetout it is like you are going to the place, pick up a stone, and make a note that you should pickup another stone. Then you deliver the stone. After that you look at your notes and see that you should pick up at stone. And you can do this 1000000 times since you are only carrying one stone at a time.

Categories