2

So basically I want to asynchronously execute 2 synchronous promises. Like so:

(function foo() {
  var newValues = ...;
  this.getMessages().then((msgs: Array<any>) => {
    this.saveMessages(msgs.concat(newValues));
  });
})()

Now since I don't want to wait for saveMessages to finish execution I didn't add a .then() at the end. Since I don't care about the result. I just want to async function to execute so at some point we have the messages offline.

BUT I fear that the promise might get garbage collected since by the standard (as far as I know) you always need a .then for promises.

So my question is whether I need to add an empty .then to make sure it's not garbage collected too early and the function wouldn't execute correctly. Like so: this.saveMessages(msgs.concat(newValues)).then();?

And is it the same in the browser and NodeJS?

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
Marc Borni
  • 384
  • 1
  • 17
  • 2
    _...promise might get garbage collected..._ - What do you mean by this? Also, what is the problem that you're facing? – maazadeeb Jan 23 '19 at 06:39
  • 1
    For the promise to resolve, you don't need to add `.then()`. – Abhishek Menon Jan 23 '19 at 06:52
  • I think you mean `asynchronously` as opposed to `synchronous context ` – cantuket Jan 23 '19 at 08:29
  • Also be aware of how you use the word ‘context’ in JS because it has a very specific meaning, which can cause confusion when used... well out of context ;) ... http://ryanmorr.com/understanding-scope-and-context-in-javascript/ – cantuket Jan 23 '19 at 08:47

4 Answers4

3

BUT I fear that the promise might get garbage collected since by the standard (as far as I know) you always need a .then() for promises.

No. There does not exist any such standard -- to my knowledge -- which states that the GC will free up the memory allocated to the a Promise object if a .then() is not attached to it.

Promises will get executed regardless of whether a .then() -- or a .catch() for that matter -- is attached to it.

So my question is whether I need to add an empty .then() to make sure it's not garbage collected too early and the function wouldn't execute correctly.

No you do not need an empty .then()

And is it the same in the browser and NodeJS?

Yes, as long as you use same Promise implementation on both the runtimes.


So, you would be completely fine with this:

function foo() {
  var newValues = ...;
  this.getMessages().then((msgs: Array<any>) => {
    this.saveMessages(msgs.concat(newValues));
  });
}

But, You should consider attaching a .catch() to it to handle any errors. If you don't, you will have UnhandledPromiseRejectionWarning. In future versions of Node, it can crash your whole app. Read about it here

EDIT

Even if the parent has nothing to process or execute, the program will wait till each of the 100 promises has resolved. Try this:

const n = 100;

const sleep = (id, seconds) =>
    new Promise(resolve => {
        setTimeout(() => {
            console.log(`promise with id ${id} is resolving`);
            resolve();
        }, seconds * 1000);
    });

for (let i = 0; i < n; i++) {
    sleep(i, 1 * i);
}

console.log("Last line of parent");
Anand Undavia
  • 3,493
  • 5
  • 19
  • 33
  • Okay, so Garbage Collection seems to not be an issue (judging by all the answers in this question). But what about "execution context completing [...] before the Promise has resolved" (see answer by cantuket): https://stackoverflow.com/a/54322111/3955332 ? – Marc Borni Jan 24 '19 at 08:29
  • 1
    That answer is wrong. I will add a snippet which will help you identify that the program does not end until each of the promises has finished. – Anand Undavia Jan 24 '19 at 09:45
  • 1
    @cantuket Exactly -- it prints all the logs of promise being resolved even if the *Last line of parent* is executed first. But based on your answer, it should just print *Last line of parent* and exit -- because there is no one waiting for those promises to be executed – Anand Undavia Jan 24 '19 at 10:30
  • Interesting. Thanks for the extra snipped. Let's ask the "opposing party" how this plays into execution context. – Marc Borni Jan 24 '19 at 10:33
  • @AnandUndavia thank you for correcting me. I had even started setting up my example in Node.js so I would have gone on just as ignorant to this if you hadn't corrected me. (palm in face) – cantuket Jan 24 '19 at 11:01
0

you can use this code and return your first promise and second promise is run sync.

function foo() {
  var newValues = ...;
  return this.getMessages().then((msgs: Array<any>) => {
    this.saveMessages(msgs.concat(newValues));
  });
}
mohammad javad ahmadi
  • 2,031
  • 1
  • 10
  • 27
0

No, the Promise knows nothing about how you're handling its completion until it gets there.

You don't need to worry about garbage collection preemptively, Javascript is smarter than that, but you do have to worry about the global execution context completing, as in, your program closing the connection, before the Promise has resolved, which would then cancel the promise and perform garbage collection.

Since you're running it asynchronously, if you want to guarantee the program awaits the completion of that Promise without making everything synchronous you can always hold the completion state of the Promises on a scoped variable or use an event emitter...

var imDone = false;

function foo() {
  var newValues = ...;
  this.getMessages().then((msgs: Array<any>) => {
    this.saveMessages(msgs.concat(newValues))
         .catch(err=> { console.log(err); imDone = true; })
         .then(()=> imDone = true);                           
  }).catch(err=> console.log(err));
}

....

if (imDone) {
  // finish execution, res.send() or res.end() in Node
} 
cantuket
  • 1,582
  • 10
  • 19
  • This is what I have been looking for: Execution context might be the problem instead of Garbage Collection. – Marc Borni Jan 24 '19 at 08:34
  • Could you please provide me with some of your sources or articles that go more in depth on how to handle execution context with promises in a sync function? – Marc Borni Jan 24 '19 at 08:34
  • You've caught me right before bed so I'll have to give you a more thorough explanation tomorrow, but in general the *"How"* of your question is pretty simple, conceptually and in application, just think about execution context as your computer (metaphor) and right now your program is *pressing the power button* vs using a proper shut-down procedure. We want to create that alert that says *"Hey do you want to save you Whatever Doc?"* before it powers down. You'll need to create proper flow control in your program, which is second nature once you're doing a lot of async programming. – cantuket Jan 24 '19 at 09:37
  • 1
    *either its parent function calling return or your program closing its thread entirely, before the Promise has resolved, which would then cancel the promise and perform garbage collection.* Wrong. The program would not exit until reach of the promises has been resolved -- or rejected. – Anand Undavia Jan 24 '19 at 09:44
  • The *"Why"* of some of these topics will get pretty academic and abstract pretty quickly, most of which won't immediately applicable to this problem, but I think if you're serious about learning JS its well worth digging deeper... [Promise: Completion Event](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md#completion-event)... [Promises: Cain Flow](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md#chain-flow). These are parts an online book by Kyle Simpson, which goes into the nuts-and-bolts of JS – cantuket Jan 24 '19 at 09:44
  • @AnandUndavia that is definitely not true. JS will not wait for every `Promise` before completing if, in say node.js, you call `res.end()`... [node keeps a reference count of scheduled async requests... Simply creating a promise or event emitter does not increase the reference count](https://stackoverflow.com/a/46916601/3329866) – cantuket Jan 24 '19 at 09:54
  • @cantuket Many thanks again for the resources and your simple explanation. I think in the end it boils down to apparently whether we have a special case like ending a response in NodeJS or a simple example like provided by AnandUndavia in his example (from his answer). PS: I updated my question to have a self-calling JS function. So this wouldn't play nice with your idea to use `if (imDone) { // finish execution }` -> could you please make a statement on how this impacts `imDone`? – Marc Borni Jan 24 '19 at 10:39
  • @AnandUndavia is correct about this situation and I'm an idiot *"parent function calling return"* (Apologies @AnandUndavia). In the situation of say a node application time you stop execution `res.end() or res.send()` it will destroy any pending promises. – cantuket Jan 24 '19 at 10:45
  • I don't think you should mark mine as correct in the current state ("parent function calling return" ), but if you are running a node program you will need to have a condition to determine if that Promise has resolved before finishing execution. – cantuket Jan 24 '19 at 10:50
  • As for your refactoring to an IIFE, that doesn't really affect anything, but I updated my example to show how you would need to use the `imDone` variable to control when your program finishes execution. (not a very elegant solution though, an event emitter would be preferable) – cantuket Jan 24 '19 at 10:58
0

You are not executing the code in the right way. Promises should be handled in the right way.There is no issue with the garbage collector freeing up the space. Your code can be written in this manner which would increase the readability and make the code easier to understand.

// This is pseudo synchronous execution of the code
function foo() {
  var newValues = 'testing';
  return this.getMessages()
  .then((msgs: Array<any>) => {
    // here return is important
    return this.saveMessages(msgs.concat(newValues));
  });
}

You can reduce the complexity by using async/await feature:

aync function foo() {
    try {
        var newValues = 'testing';
        const msgs = await this.getMessages()
        await this.saveMessages(msgs.concat(newValues))
    } catch (ex) {
        // handle your error here
    }
}
Supermacy
  • 1,429
  • 15
  • 24
  • Can you elaborate on *here return is important*? – cantuket Jan 23 '19 at 07:59
  • For pseudo synchronous execution of the code we have to make sure that ```.then()``` which is implemented should be run after the ```this.saveMessages(```(I am assuming this.saveMessages will return a Promise) function. If we will not use return then ```this.sageMessages``` will be thrown out of the stack. So the execution of code will not be synchronous – Supermacy Jan 23 '19 at 08:06
  • Ah, I see. I think there's a little confusion with the question because the OP has some contradictory language in the question `synchronous context` and `I don't want to wait for saveMessages to finish`. – cantuket Jan 23 '19 at 08:21
  • From what I gather they want `foo()` to execute async, with the caveat of *it must complete at some point*. I do think awaiting `foo()` itself somewhere at the end of execution, the way you have it, is the most elegant way handle it though. – cantuket Jan 23 '19 at 08:26
  • Hello. So in my question the scenario is without async / await -> I know I'm save by using async / await but wanted to know what happens if I don't want to await a async function inside a sync function – Marc Borni Jan 24 '19 at 08:32
  • @MarcBorni No problem there, as long as the `async` function isn't being awaited itself then only the steps inside the `async` function will be synchronous, while the function itself will execute asynchronously since it returns a Promise. This is the preferred syntax if you a willing to use it IMO. (though maybe take my advice with a grain of salt after that last blunder and test for yourself : / ) – cantuket Jan 24 '19 at 11:21