-1

I am quite a beginner with javascript and node.js, so forgive me if the question can be considered as too simple.

I was wondering, if I have a function that returns a Promise, and in its resolve() it calls again the same function in a sort of recursion, can this cause a stack overflow in case it does not get resolved?

You can imagine it as it follows:

var someVariable = await myFunction(someInput)

async function myFunction(myInputValue) {

    return new Promise(function(resolve, reject) {

        // do some computation
        if (someCondition) {
            resolve(true)
            return
        } else {
            resolve(myFunction(myInputValue))
            return
        }
    })
}

I was asking this since I noticed the return instruction gets executed, and this should (in my opinion) deallocate the function's context stack and avoid getting issues like stack overflows. Am I missing something and then I am risking issues or am I right and this can be considered quite safe as practice?

  • why is `myFunction` `async`? it never `await`s ... so `async` is redundant – Bravo Jun 10 '22 at 09:16
  • to test if there will be a stack overflow ... simply change `if (someCondition) {` to `if (false) {` - then you'll see exactly what happens if `someCondition` is never met - testing things like this can be so easy, if only one actually tries – Bravo Jun 10 '22 at 09:18
  • @Bravo I put `async` because in my real code it needs to `await`, so you can also not consider it in here. By the way, I tried to execute it in that way and it does not get any troubles, but I was asking it to be sure. As I said, I am new to node.js so if anyone knew it for sure, having a better knowledge than me, I would have felt safer. – Kronos_Rob Jun 10 '22 at 09:24
  • that's the problem with real code vs minimal code ... sometimes the minimal code is too minimal to make a reasonable suggestion – Bravo Jun 10 '22 at 09:28
  • @Bravo yes, it is sure. But the question was not on that, so it was useless to put my whole code when the actual question was on the function's stack context management by node.js in this particular case. – Kronos_Rob Jun 10 '22 at 09:33
  • I think it returns TypeError. – jkalandarov Jun 10 '22 at 10:23
  • @jkalandarov I get no errors from this, and even if I force it to make as said by Bravo I don't get any problems. I was only wondering if it can lead to problems if it gets stuck enough in the `else` section, e.g. stack overflow or something else – Kronos_Rob Jun 10 '22 at 10:29
  • `async` is syntactic sugar for returning a promise without writing the code for returning a promise so: _either_ use `async function(...) { normal code here, with a normal direct reteurn of the function's output }` _or_ `function() { return new Promise(...); }`. If a function explicitly returns a promise, you don't use `async`. (Likewise, `await` is simply the syntactic equivalent of a `.then()` handling call, without having to write the code for chaining `then` functions). – Mike 'Pomax' Kamermans Jun 10 '22 at 17:29
  • @Mike If we're talking about the general case, a function can explicitly return a Promise and still use `async` idiomatically. This is particularly true if calling `await` earlier in the function and then using a Promise-returning utility like `Promise.all`...or, for that matter, when using `new Promise` out of callback-adapting necessity when using `await` earlier in the function. – Jeff Bowman Jun 10 '22 at 17:48

1 Answers1

-1

myFunction is an async function, so you can treat it as a function that always returns a Promise, and you can call it recursively.

It is safe to resolve to a Promise in a Promise constructor:

The resolutionFunc value parameter can be another promise object, in which case the promise gets dynamically inserted into the promise chain.

And it is also safe to return a Promise from an async function:

The return value of an async function is implicitly wrapped in Promise.resolve - if it's not already a promise itself (as in the examples).

...where Promise.resolve is described as returning:

A Promise that is resolved with the given value, or the promise passed as value, if the value was a promise object.

However: There should be no reason to return a new Promise in your use of async here: The async function already wraps anything it can return in a Promise, so you can skip the explicit Promise construction antipattern. (Reserve your use of the Promise constructor only when you are adapting a callback-style call into promises, which can happen in async and non-async functions.)

var someVariable = await myFunction(someInput)

async function myFunction(myInputValue) {
  // do some computation on myInputValue that awaits something

  if (someCondition) {
    return true;
  } else {
    return myFunction(someModificationOf(myInputValue));
  }
}

You'll still need to check that this recursive case is safe based on someCondition and whatever recursive modifications you do to myInputValue: If you don't bail out of your recursive case, then you might encounter a stack overflow, or you might run out of heap memory or spin forever. (In async functions, the function runs synchronously up until the first await, but Promise handlers are always called with an otherwise-empty stack per Promises/A+ 2.2.4 and ES6.)

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • @Mike I'll concede that if you need to consume a callback-style function in an `async` function, you may need to use a `new Promise` as you would anywhere else, but I'm not sure that's relevant to the OP's specific question and I'm not sure your edit makes the general statement clearer. – Jeff Bowman Jun 10 '22 at 17:36
  • Your answer is not just going to be read by the OP, it's also going to be read by every future visitor who finds it, so it's always better to be correct when explaining a confusing (to folks new to it) concept like async vs explicit promises. The way it was written suggested and async function should never have a promise anywhere in it, which isn't strictly true (especially when needing to wait for timeouts or otherwise promisified-synchronous code inside async functions). – Mike 'Pomax' Kamermans Jun 10 '22 at 17:38
  • @Mike Oh, don't get me wrong, I'm fully agreed that the answer is for future visitors...but I think it's _less_ clear post-edit. Here, if we want to split hairs, I think I'll need to edit further. [Done.] – Jeff Bowman Jun 10 '22 at 17:40