0

Two points of confusion:

  1. How are function frames preserved and disposed?

Example:

function foo() {
  var a = ...;
  setTimeout(function() {
    console.log(a);
  },50);
  return a;
}

In this example, the inner function in setTimeout is referencing the outer variable a, even after foo has returned. Coming from the Java world, I am confused as to how this can happen? How does the foo stack frame get stored for the inner function's use, and when does it get popped off the stack?

  1. Multiple async/promise "returns"

Example:

async function foo2() {
  var p = new Promise() {
    setTimeout(function() {
      p.reject(null);
    },60000);
    p.resolve(await dbcall.execute());
  }
  return p;
}

And somewhere else:

foo2.then(resolve, reject) {
  ...
}

Suppose the timeout call happens first, and then the dbcall returns. The promise will potentially be resolved twice.

Question(s): After the timeout call rejects, will the function still keep (a)waiting for db call to return, and then execute whatever code comes after await? And what happens with a second fulfillment of the promise (ie the resolve call when await has completed) Does only the first resolve/reject get processed?

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
  • *"Does only the first resolve/reject get processed?"* Seems easy enough to test out.... – epascarello Jun 01 '21 at 17:39
  • 1. [variable hoisting](https://www.geeksforgeeks.org/javascript-hoisting/) and [closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) 2. It's a promise, which means it checks for it to be resolved, the promise is kind of an object of its own. [Both your THENs get processed](https://javascript.info/promise-chaining); the second one knows it completed and just right away resolves (or rejects). It's like a Task, if Java has that sort of thing (C# person here) – Nikki9696 Jun 01 '21 at 17:39
  • `Suppose the timeout call happens first, and then the dbcall returns. The promise will potentially be resolved twice` not true.. the first resolve/reject would **FULFIL** the promise – The Bomb Squad Jun 01 '21 at 17:44
  • 1
    I glossed over the *"How are function frames preserved and disposed?"* question: Each function creates a new scope. Scopes are nested and referred to from the inside out, as you would expect. Functions are objects - as long as they are alive, their references will not be garbage-collected. This applies to functions whose execution is delayed. Caveat: An outer-scope value could change before the function referencing it runs. The scope only captures the variable reference, you'll always get the current value. – Tomalak Jun 01 '21 at 18:43
  • @Tomalak Thanks! – ControlAltDel Jun 01 '21 at 18:51

1 Answers1

1

Coming from the Java world, I am confused as to how this can happen?

It's called a closure.

All variables that are referenced by a function remain in scope for that function, no matter where it is, or when it is executed. It's one of the fundamental principles in JavaScript.

Your second example does not make sense.

  • The only reason to declare a function as async is if you wanted to use await inside.
  • If an inner function returns a promise, return that promise. Don't create a new one (see the Promise construction antipattern).

So it should simply read:

function foo2() {
  return dbcall.execute();
}

or, in fat-arrow syntax

const foo2 = () => dbcall.execute();

In order to generate a timeout, you can use Promise.race().

const timeout = (promise, ms) => Promise.race([
    new Promise((_, reject) => setTimeout(() => reject('timeout'), ms)),
    promise
]);

timeout(foo2(), 60000).then(foo2Result => {
    // success
}).catch(error => {
    // either 'timeout' or an error from dbcall
});

But if you wanted to wrap it in a new promise, you could - at the expense of composability:

const foo2 = () => new Promise((resolve, reject) => {
    dbcall.execute().then(resolve).catch(reject);
    setTimeout(() => reject('timeout'), 60000);
});

After the timeout call rejects, will the function still keep (a)waiting for db call to return

Yes, both things are executed independently. Unless you build a mutual cancellation mechanism, both the setTimeout() and the dbcall.execute() will run to completion.

what happens with a second fulfillment of the promise (ie the resolve call when await has completed) Does only the first resolve/reject get processed?

Promises that are settled (resolved or rejected) will not change their final value anymore, no matter how many internal functions call reject or resolve after the fact.

Tomalak
  • 332,285
  • 67
  • 532
  • 628