Why does javascript ES6 Promises continue execution after a resolve?

39,198

Solution 1

JavaScript has the concept of "run to completion". Unless an error is thrown, a function is executed until a return statement or its end is reached. Other code outside of the function can't interfere with that (unless, again, an error is thrown).

If you want resolve() to exit your initializer function, you have to prepend it by return:

return new Promise(function(resolve, reject) {
    return resolve();
    console.log("Not doing more stuff after a return statement");
});

Solution 2

The callbacks that will be invoked when you resolve a promise are still required by the specification to be called asynchronously. This is to ensure consistent behaviour when using promises for a mix of synchronous and asynchronous actions.

Therefore when you invoke resolve the callback is queued, and function execution continues immediately with any code following the resolve() call.

Only once the JS event loop is given back control can the callback be removed from the queue and actually invoked.

Solution 3

The resolve() function is not like return at all. It simply indicates that the argument of the callback function which was registered with then() method, is now ready and the callback function can potentially leave the Job queue (or the Micro task queue) and enter the main JS call stack, but that only happens when all synchronous codes and the asynchronous codes that entered the queue before this one finished running. console.log("Not doing more stuff after a return statement"); this statement in your code is a synchronous code, and it has priority over the asynchronous codes. That's why it runs first

Share:
39,198
Ludwig Van Beethoven
Author by

Ludwig Van Beethoven

Updated on February 12, 2022

Comments

  • Ludwig Van Beethoven
    Ludwig Van Beethoven about 2 years

    As I understand a promise is something that can resolve() or reject() but I was suprised to find out that code in the promise continues to execute after a resolve or reject is called.

    I considered resolve or reject being an async-friendly version of exit or return , that would halt all immediate function execution.

    Can someone explain the thought behind why the following example sometimes shows the console.log after a resolve call:

    var call = function() {
        return new Promise(function(resolve, reject) {
            resolve();
            console.log("Doing more stuff, should not be visible after a resolve!");
        });
    };
    
    call().then(function() {
        console.log("resolved");
    });
    

    jsbin

    • Admin
      Admin about 9 years
      Reasonable question, but then again, JS just executes one statement after another like you tell to it to. resolve() is not a JS control statement that magically would have the effect of return, it's just a function call, and yes, execution continues after it.
    • Gabriel Glenn
      Gabriel Glenn about 6 years
      This is a good question, and even after reading all the responses, I'm not sure about the best practices...
    • Giuseppe Bertone
      Giuseppe Bertone over 4 years
      I think the misunderstanding comes from what exactly you are terminating with resolve(): the promise IS resolved just after you call resolve(), but as already said by others, this does not mean that the function that have terminated the promise had terminated its duty too, so it continues until it reaches a "normal" termination.
  • thefourtheye
    thefourtheye about 9 years
    The callback queuing is documented in A+ Specs or in ES6?
  • Felix Kling
    Felix Kling about 9 years
    @thefourtheye: The event loop specification is actually part of HTML5 now. ES6 defines an internal method called EnqueueJob, which is invoked by .then.
  • Felix Kling
    Felix Kling about 9 years
    @thefourtheye: Actually, ES6 also seems to define queues: people.mozilla.org/~jorendorff/…. I guess the related to the event loop one way or the other.
  • Alnitak
    Alnitak about 9 years
    @FelixKling thanks for the links - I knew that this was how it worked, but couldn't quote chapter and verse
  • Alnitak
    Alnitak about 9 years
    Hi Felix - I think that this is only part of the story - the other part is that resolve() is itself an async function. As we saw in the other (deleted) answer, some people believe that calling resolve will immediately run any callbacks.
  • Esailija
    Esailija about 9 years
    @Alnitak resolve itself is not asynchronous, it's completely synchronous. Although using strictly ES6 API it's not observable whether it's synchronous or asynchronous.
  • Alnitak
    Alnitak about 9 years
    @Esailija ok, perhaps I was unclear. Some people believe that calling resolve will result in any registered callbacks being immediately invoked such that they're part of the current call stack. That's not true, instead it just queues the callbacks (and you're right, it's not async, but it just does its thing and terminates immediately)
  • Benjamin Gruenbaum
    Benjamin Gruenbaum about 9 years
    @FelixKling it's microtasks/macrotasks, here is the part in the spec that "defers" "When there is no running execution context and the execution context stack is empty, the ECMAScript implementation removes the first PendingJob from a Job Queue and uses the information contained in it to create an execution context and starts execution of the associated Job abstract operation."
  • Felix Kling
    Felix Kling about 9 years
    @Alnitak: I understand what you are saying. I just interpreted it as why does the console.log show up at instead instead of why does it show up in that order. In so far, what resolve does and how promises is irrelevant to how I interpret the question. But of course it's still important to know in the context of promises. One of the reasons I upvoted your answer :)
  • Don Hatch
    Don Hatch over 7 years
    @Bergi, in your edit, you say "return resolve();" which seems unusual. In order to convince myself there's nothing of importance going on there, I had to read the documentation and see that (1) resolve() doesn't appear to return anything of consequence, and (2) the initialization callback's return value doesn't appear to be used. Wouldn't it be clearer to say "resolve(); return;" thereby avoiding this distraction?
  • Bergi
    Bergi over 7 years
    @DonHatch Yes, that's fine as well. Maybe I'm too familiar with promises to see it as a distraction :-) However, if (err) return reject(err) is a common idiom where the return value is not of interest either.
  • xploreraj
    xploreraj over 4 years
    Your suggestion of return saved me from brain haemorrhage. You earned an upvote for that.