Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Weird stack overflow in async functions

I'm getting some unexpected behavior when using async functions.

testAsync(0);
async function testAsync(i){
  i++;
  console.log("async " + i);
  if (i < 4)
    await testAsync(i);
}

results in 

[4:55:56 AM]async 1
[4:55:56 AM]async 2
[4:55:56 AM]async 3
[4:55:56 AM]Terminating app: Memory fault (stack overflow?)

 

I would expect a lot more than three calls before reaching a stack overflow. This is indeed the case when using a synchronous function:

testSync(0);
function testSync(i){
  i++;
  console.log("sync " + i);
  if (i < 25)
    testSync(i);
}

This gets all the way to 22 before termination.

 

The thing I find really surprising though is that you can actually go much higher than that, by using an asynchronous function with a sleep function:

testAsyncWithSleep(0);
async function testAsyncWithSleep(i){
  i++;
  console.log("async/sleep " + i);
  await SleepAsync(1); // fixes the issue???
  if (i < 400)
    await testAsyncWithSleep(i);
}

async function SleepAsync(milliseconds) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true);
    }, milliseconds);
  });
}

 This gets up all the way to 126 before being terminated, but it's also with a different error message:

[...]

[5:07:49 AM]async/sleep 126
[5:07:53 AM]Fatal Jerryscript Error: ERR_OUT_OF_MEMORY

 

So what gives? Why is the stack limit only three when there's no sleep during it?

P.S. I'd find it really useful if the stack overflow message could be less vague, as it's really difficult to debug with basically zero information on what's wrong.

Best Answer
0 Votes
1 REPLY 1

Hello,

 

I had the error Terminating app: Memory fault (stack overflow?) too with RxJs on the watch side. I had to add your sleep function to force the execution to be async and the problem was resolved for now.

 

// INFO: delay the subscription to the observables.
setTimeout(() => {
    // INFO: Merge changes
    messagesObservable
        .pipe(
            map(x => stateService.update(state => { return { ...state, ...x } })),
            throttleTime(1000)
        )
        .subscribe({
            async next(x) {
                await forceSwitchContext(); // <--------- the fix

                ui.render(x);


                // vibration.start('ring');
            },
            error: x => console.error(x)
        });
}, 1);
render: (state) => {
            if (!state) {
                throw new Error('The argument state was undefined');
            }

            if (!isInitialized) {
                throw new Error('The UI service was not initialized.');
            }

            const { items: localItems } = state;

            // INFO: Update the local items.
            items.clear();
            localItems.copyTo(items);

            // INFO: Trigger the update the virtual list.
            list.length = items.length; // <------ CRASH HERE without the fix
        }

 

Best Answer
0 Votes