ES6承诺阻止页面加载

5

给定以下测试代码:

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 中进行了测试,根据 这个表格 ,Promise 应该已经完全实现。 这里的 Fiddle(警告:会阻塞标签页)。

这样一个困难循环总是会阻塞页面,无论它在何时何地运行。Promise 不能提供并发/并行! - Bergi
2个回答

8
这段代码应该是异步运行的,但这取决于你正在讨论的代码部分。在您的Promise执行程序中(您传递给new Promise的函数),代码不会异步运行。从§25.4.3.1第10步可知:
“让完成为Call(executor,undefined,«resolvingFunctions。[[Resolve]],resolvingFunctions。[[Reject]]»)。”
请注意,这里没有关于new Promise调用执行程序的异步性。(它是一个“Call”,而不是“EnqueueJob”。)
异步保证适用于then,而不是new Promise。(§25.4.5.3§25.4.5.3.1。)承诺是即使承诺已经解决,您的回调函数也不会与then调用同步调用,它将在此之后被调用,因为它将通过“EnqueueJob”进行安排。
所以在您的代码中会发生以下情况:
1. 您调用new Promise,它同步调用执行程序。 2. 最终执行程序返回并完成p,即new Promise完成。 3. 您调用p.then(...);该调用排队一个作业来调用您的回调函数并立即返回。 4. JavaScript作业队列中的当前作业运行到完成。 5. 执行调用回调的作业,调用回调。

这是我之前不知道的一些新的异步定义吗?它怎么可能是异步的,但仍然在启动它的同一个线程上运行?或者我错过了一些基本的东西(这可能会让我感到尴尬)? :-) - paxdiablo
@paxdiablo:与ajax相同的方式是异步的:安排一个新任务,当前任务的关闭运行到完成,然后运行新任务。 - T.J. Crowder
1
我应该说:“...然后新任务在同一线程上运行。” :-) (我可能应该使用JS术语“job”而不是HTML5术语“task”,但它们是相同的东西...) - T.J. Crowder
现在更有意义了,Promise 本身不是异步/延迟的,只是稍后调用 resolve/reject 函数的调用是异步/延迟的。 - paxdiablo
@paxdiablo:是的。我认为假设是承诺执行器函数中的代码将启动其他异步进程。then 上的异步保证是为了避免混乱-调用 then 的代码应该知道回调是立即发生还是稍后发生。对于不保证异步 then 回调的承诺(我在看你,jQuery),调用 then 的代码无法在调用之前不检查承诺的状态而知道发生什么,这只是愚蠢的。 :-) - T.J. Crowder
显示剩余2条评论

2

异步不等于并行

JavaScript 是基于事件和单线程的。Promise 也无法改变这一点。只有内置的浏览器函数才能真正实现并行操作。

因此,在 JavaScript 中,当我们说在 p.then(f) 中调用 f 是“异步”的时候,它只是意味着在同一个线程中稍后执行,即在我们现在所在的运行到完成任务之后的下一个任务中执行。这是一件好事情,并且这也是为什么你不需要使用互斥锁来锁定数据(没有并发访问导致的数据竞争)。

对于 JavaScript 的这种混淆似乎很常见,以至于规范正在更改他们的语言以使这种区别更清晰。

所有这些说法,只有关于仅浏览器功能运行事物并行的部分不再完全正确。要真正在JavaScript中并行运行密集计算,请查看Web Workers,这是一个新的良好隔离的线程启动概念,它可能与您运行的不同选项卡一样运行,除了您可以通过消息与它们交谈。由于它们与您不共享数据空间,因此它们不会破坏JavaScript线程保证(或更改我在此处所说的任何内容)。
承诺的重点不是提供并发访问,而是解决回调的落后性,并使代码更易于理解,也许更重要的是,解决异步操作链(无论它们是否并行运行)的错误传播。
承诺还使启动和等待多个异步操作变得微不足道(使用Promise.allPromise.race),这是一件非常复杂且很难正确地处理(没有错误)和理解使用回调。

1
JavaScript是基于事件和单线程的...在浏览器中。 :-) (以及在NodeJS中) - T.J. Crowder
什么情况下它不是基于事件和单线程的?JS中没有锁构造,语言是按顺序执行的。 - jib
1
任何时候,只要主机环境不这样做。特别是,JVM 上的 JavaScript(Rhino、Nashorn)是多线程的。JavaScript,语言本身,在线程方面基本上是沉默的。主机环境可以是多线程的,也可以不是。我说“基本上沉默”,因为完成运行与 一个 线程有关(但不要求它是唯一的)。 - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接