Promise#all vs sequential waiting-时间分辨率的差异



在下面的代码段中,当使用promise#all并按顺序等待每个promise时,解析时间是不同的。我想深入了解发生了什么,因为在处理性能时,这似乎是一个警告。

*编辑:添加了另一个没有函数调用的案例,可能我错过了等待的工作原理

// Code goes here
window.onload = function() {

let p = new Promise((res, rej) => {
setTimeout(() => {
res('p OK')
}, 5000);
})

let p2 = new Promise((res, rej) => {
setTimeout(() => {
res('p2 OK')
}, 5000);
})

let p3 = new Promise((res, rej) => {
setTimeout(() => {
res('p3 OK')
}, 5000);
})

async function pp() {
return new Promise((res, rej) => {
setTimeout(() => {
res('pp OK');
}, 5000);
});
}

async function a() {
let out = await Promise.all([p, pp()]);
return `#a ${out}`;
}

async function b() {
let out1 = await p;
let out2 = await pp();
return `#b ${out1} ${out2}`;
}

async function c() {
let out1 = await p;
let out2 = await p2;
let out3 = await p3;
return `#c ${out1} ${out2} ${out3}`;
}

let out1 = document.getElementById("out1");
let out2 = document.getElementById("out2");
let out32 = document.getElementById("out2");
const date = new Date().getSeconds();
a().then(x => console.log(x)).then(() => out1.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
b().then(x => console.log(x)).then(() => out2.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
c().then(x => console.log(x)).then(() => out3.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<p id="out1">
Promise#all  
</p>
<p id="out2">
Sequential awaits
</p>
<p id="out3">
Sequential awaits without executing fnc
</p>
</body>
</html>

一些可以澄清问题的要点:

  • 传递给new Promise的构造函数回调函数会立即执行,这意味着setTimeout延迟在那个时刻开始,无论您是否要等待它。

  • 因此,pp2p3创建的超时都是在这些变量初始化后立即启动的。由于它们都将在5秒后超时,因此相应的承诺(pp2p3)将在大约相同的时间得到解决。同样,无论您是使用awaitthenPromise.all,还是忘记这些承诺而不使用它们,这都是独立的

  • 相比之下,函数pp只是一个函数。这不是承诺。只有当您实际调用该函数时,您才能创建一个具有相应超时的promise。

  • await使async函数立即返回,返回promise。这意味着函数的其余代码被推迟,而不是其他Javascript代码的其余部分:函数返回,并在该函数调用后继续执行。当调用堆栈为空时,将处理不同的事件队列。所以它没有阻塞。一旦传递给await的promise解析,就会神奇地发生一些事情:async的函数执行上下文被恢复,并且执行将继续,直到下一个await。如果没有更多的await可执行,并且函数到达其末尾,则它返回的promise(当遇到第一个await时)将被解析

  • Promise.all不影响个人承诺何时解决。它创建了一个新的promise,当所有给定的promise都已解析时,该promise就会解析。如上所述,您的案例中的承诺(pp2)大约在同一时刻解决,因此Promise.all也将在大约5秒后解决。

  • 如果你做await pp(),你只会当场做出承诺,而不是更早。因为前面有一个await p,所以执行pp()需要5秒钟。因此,在执行第二个await之前,相应的超时不会开始。这就是为什么await p; await pp()需要两次5秒才能解决的原因。

  • 当您执行await p; await p2时,情况有所不同。这两个承诺都已经创建,并且它们的超时已经开始。因此,当5秒后第一个await结束时,JS将到达第二个await,并发现p2也被解析。不会有任何额外的等待期(除了异步管理,await总是将事件放入微任务队列)

我认为有了这些元素,你就可以正确地描述代码将如何执行,以及你得到的输出是如何达到预期的。

在第一个函数中,它立即执行pp以启动promise超时。在第二个函数中,它等待p完成,然后执行pp以启动承诺超时。

await关键字"阻止*"执行,直到promise解析,然后继续执行下一行。

回复:更新

在您的第三个示例中,所有的承诺都是在此时开始的。await关键字等待,直到promise得到解析。因为你所有的承诺都是同时开始的,它们都会在一个很小的三角洲内解决。当你点击await p2时,它可能已经解决了。与p3相同。

在第二个示例中,直到p完成,pp才会启动。

[*]它并没有真正阻止执行,但这就是它对代码的影响。

最新更新