0

The following javascript function is from You Don't Know JS: Async & Performance. According to my understanding, the first comment // start doing something that could take a while is misleading. The section of the code where something is actually done possibly asynchronously is in the function passed into the Promise constructor.

function foo(x) {
    // start doing something that could take a while *misleading comment*

    // construct and return a promise
    return new Promise( /* executor */ function(resolve,reject){
        // eventually, call `resolve(..)` or `reject(..)`,
        // which are the resolution callbacks for
        // the promise.
    } );
}

I would fix it in the following way:

function foo(x) {
    // construct and return a promise
    return new Promise( /* executor */ function(resolve,reject){
        // start doing something that could take a while
        // then foo returns the newly created Promise
        // eventually, call `resolve(..)` or `reject(..)`,
        // which are the resolution callbacks for
        // the promise.
    } );
}
Roland
  • 7,525
  • 13
  • 61
  • 124
  • 2
    Yes, your construction is probably better, though code can be executed before you create the promise too. I'm not sure what question you're asking here that you don't already know the answer to. – jfriend00 Apr 23 '17 at 11:05
  • 1
    The code is too abstract to really find fault with it, or to "declare" one is better than the other – Jaromanda X Apr 23 '17 at 12:04
  • 1
    The answer is: nowhere. The promise executor function runs synchronously. It is up to you to launch an asynchronous operation from there that eventually calls `resolve` or `reject` (probably using callbacks). It's a common misunderstanding to think the promise executor function itself somehow runs asynchronously - which is not true - and I worry your question (and the confirming answer, though correct) may lead people astray. Can you edit it to be clearer about this? – jib Apr 23 '17 at 15:25
  • @jfriend00 Code can be executed before you create the promise, but then you would have to introduce an auxiliary variable to call resolve or reject, [see this comment](https://github.com/getify/You-Dont-Know-JS/issues/1015#issuecomment-299166020) – Roland May 15 '17 at 07:05

2 Answers2

2

Yes, this should be fixed (thanks for filing this issue).

This reminds me a bit of the difference between the deferred pattern and the revealing constructor pattern. Starting the asynchronous task inside the promise constructor callback has two advantages:

  • If it throws synchronously (e.g. a syntax error, a typo in the method invocation etc), the exception will be caught implicitly and reject the promise
  • The resolve and reject callbacks are already in scope to be passed to the asynchronous process as callbacks.
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

The answer is: nowhere is code executed asynchronously in a promise. *

While the second form you show is slightly more advantageous, this is solely because it handles errors more consistently (expecting callers to handle both exceptions and rejections is poor form).

Addressing your main confusion: The promise executor function runs synchronously. From MDN:

The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object).

And the spec:

Returning from the executor function does not mean that the deferred action has been completed but only that the request to eventually perform the deferred action has been accepted.

It is up to you to launch any asynchronous operation that eventually calls resolve or reject:

new Promise(resolve => setTimeout(resolve, 2000))
  .then(() => console.log("Async"));

console.log("Sync");

Most importantly, the Promise constructor only exists to wrap legacy functions that don't support promises already. Don't use it for anything else.


*) Actually, .then guarantees that functions passed to it never run immediately, so my first statement isn't entirely correct. The soonest they can run is at the end of the same run of the event loop, in a micro-task queue. Hardly what you're after.

Community
  • 1
  • 1
jib
  • 40,579
  • 17
  • 100
  • 158
  • "The soonest they can run is at the end of the same run of the event loop, in a micro-task queue." Is the micro-task queue the same as the job-queue( as described [here](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch1.md))? – Roland Apr 24 '17 at 13:24
  • 1
    Yes. Any function you attach to a `.then` still runs as single-threaded blocking code mind you, like all other JavaScript. It merely runs later. The only way to truly run JS on another thread is to [launch a worker](http://stackoverflow.com/a/42800542/918910). – jib Apr 24 '17 at 14:44