ES6 Promise 块页面

ES6 Promise blocks page

给定以下测试代码:

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");});

此代码应 运行 异步,但它会阻止与页面的所有交互。为什么?

这在 Chrome 44 中进行了测试,根据 this table Promises 应该完全实现。

Fiddle here(警告:阻止选项卡)

This code should run asynchronously

是与否,这取决于您所谈论的代码的哪一部分。您的承诺执行器中的代码(您传递给 new Promise 的函数)是 而不是 运行 异步的。来自 §25.4.3.1,第 10 步:

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

请注意,new Promise 异步调用执行程序没有任何意义。 (这是一个“Call" rather than "EnqueueJob”。)

异步保证适用于 then,不适用于 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".

所以您的代码中发生的是:

  1. 你调用new Promise,它同步调用你的执行器

  2. 最终执行者 returns 和 new Promise 完成,给我们 p.

  3. 你打电话给p.then(...);呼叫排队一个作业来呼叫您的回调并立即returns。

  4. JavaScript 作业队列中的当前作业 运行 秒完成。

  5. 执行调用回调的作业,调用回调。

异步 != 并行

JavaScript 是基于事件和单线程的。承诺不会让你逃避。只有内置的浏览器功能才能真正并行

因此,在JavaScript中,当我们说p.then(f)中的f被称为异步时,就意味着 later 在同一个线程中,即在后续的 运行-to-completion 任务中,与我们现在所在的任务分开。这是一件好事,也是您不需要使用互斥锁锁定事物的原因(没有来自并发访问的数据竞争)。

这种关于 JS 的混淆似乎很常见,以至于规范 changing their language 使这种区别更加清晰。

综上所述,仅浏览器功能运行并行处理的部分不再完全正确。要真正 运行 在 JavaScript 中并行进行密集计算,请查看 web workers,这是一个新的隔离良好的启动线程概念,它可能 运行 在不同的选项卡中运行来自您,除非您可以通过消息与他们交谈。由于他们不与您共享任何数据 space,因此他们不会破坏 JavaScript 线程保证(或更改我在这里所说的任何内容)。

承诺

promises 的重点不是提供并发访问,而是解决回调的落后问题并使代码更易于推理,也许更重要的是,通过异步操作链解决错误传播(无论它们是否 运行并联与否).

Promises 也使得一次启动和等待多个异步操作变得微不足道(使用 Promise.allPromise.race),这是非常复杂的事情(没有错误)并仅通过回调进行推理。