Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 Promise blocks page

Given the following test code:

var p = new Promise(function(resolve, reject) {
    for(var i=0;i<10000000;++i)
        for(var y=i;y<10000000;++y)
            z = i + y;
    resolve();
});
p.then(function(){alert("resolved");});

This code should run asynchronously, but it blocks all interaction with the page. Why?

This was tested in Chrome 44, according to this table Promises should be fully implemented.

Fiddle here (warning: blocks the tab)

like image 438
Appleshell Avatar asked May 22 '15 08:05

Appleshell


2 Answers

This code should run asynchronously

Yes and no, it depends on what part of that code you're talking about. The code in your promise executor (the function you pass into new Promise) is not run asynchronously. From §25.4.3.1, step 10:

Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).

Note that there's nothing there about new Promise calling the executor asynchronously. (It's a "Call" rather than "EnqueueJob".)

The asynchronous guarantee applies to then, not new Promise. (§25.4.5.3 and §25.4.5.3.1.) The guarantee is that even if the promise is already resolved, your callback will not be called synchronously with the then call, it will be after because it will be scheduled via "EnqueueJob".

So what happens in your code is:

  1. You call new Promise, which calls your executor synchronously.

  2. Eventually the executor returns and new Promise completes, giving us p.

  3. You call p.then(...); the call queues a job to call your callback and returns immediately.

  4. The current job in the JavaScript job queue runs to completion.

  5. The job that calls to your callback is executed, calling the callback.

like image 200
T.J. Crowder Avatar answered Oct 20 '22 13:10

T.J. Crowder


Asynchronously != In parallel

JavaScript is event-based and single-threaded. Promises don't let you escape that. Only built-in browser functions have the power to do things actually in parallel.

Therefore, in JavaScript, when we say f in p.then(f) is called asynchronously, it just means later in the same thread, i.e. in a subsequent run-to-completion task separate from the one we're in now. This is a good thing, and is why you don't need to lock things with mutexes (no data races from concurrent access).

This confusion about JS seems common, to the point where specifications are changing their language to make this distinction clearer.

All that said, the part about only browser functions running things in parallel isn't entirely true anymore. To truly run intensive computations in parallel in JavaScript, look at web workers, a new well-isolated concept of launching threads that might as well be running in a different tab from you, except you can talk to them through messages. Since they share no data space with you, they don't upset the JavaScript threading guarantee (or change anything I'm saying here).

Promises

The point of promises is not to offer concurrent access, but to address the backwardness of callbacks and make code easier to reason about, and perhaps more importantly, solve error propagation through chains of asynchronous operations (regardless of whether they run in parallel or not).

Promises also make it trivial to launch and wait for more than one asynchronous operation at a time (with Promise.all and Promise.race), something that is insanely complicated to get right (without bugs) and reason about with just callbacks.

like image 26
jib Avatar answered Oct 20 '22 13:10

jib