给出以下测试代码:
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(警告:阻止标签页)
答案 0 :(得分:7)
此代码应以异步方式运行
是和否,这取决于您所谈论的代码的哪一部分。您的promise执行者中的代码(传递给new Promise
的函数)是不异步运行。来自§25.4.3.1,第10步:
让完成调用(执行者,未定义,«resolvingFunctions。[[Resolve]],resolvingFunctions。[[Reject]]»)。
请注意,new Promise
异步调用执行程序没有任何内容。 (这是“Call”而不是“EnqueueJob”。)
异步保证适用于then
,而非new Promise
。 (§25.4.5.3和§25.4.5.3.1。)保证即使承诺已经解决,您的回调 也不会与then
电话同步调用,它将在之后,因为它将通过“EnqueueJob”安排。
所以你的代码会发生什么:
您拨打new Promise
,同步调用您的执行人。
最终执行者返回并new Promise
完成,给我们p
。
您致电p.then(...)
;呼叫将作业排队以调用您的回叫并立即返回。
JavaScript作业队列中的当前作业将运行至完成。
执行调用回调的作业,调用回调。
答案 1 :(得分:2)
JavaScript是基于事件和单线程的。承诺不会让你逃避。只有内置的浏览器功能才能实现并行。
因此,在JavaScript中,当我们说f
中的p.then(f)
被称为异步时,它只是意味着以后在同一个线程中,即在随后的完成任务中,与我们现在的任务分开。这是一件好事,这就是为什么你不需要用互斥锁来锁定东西(没有来自并发访问的数据竞争)。
关于JS的这种混淆似乎很常见,规范是changing their language以使这种区别更加清晰。
所有这一切,关于只有浏览器功能并行运行的部分不再完全正确。要真正在JavaScript中并行执行密集计算,请查看web workers,一个新的隔离概念,即启动线程,它可能会在与您不同的选项卡中运行,除非您可以通过消息与它们通信。由于它们不与您共享数据空间,因此它们不会破坏JavaScript线程保证(或更改我在此处所说的任何内容)。
承诺的重点不是提供并发访问,而是为了解决回调的落后问题并使代码更易于推理,或许更重要的是,通过异步操作链解决错误传播(无论它们是否并行运行)或不)。
Promise也使得一次启动和等待多个异步操作(使用Promise.all
和Promise.race
)变得微不足道,这是一个非常复杂的事情(没有错误)和原因只是回调。