javascript - Promise#all 与顺序等待 - 时间分辨率的差异
问题描述
在下面的代码片段中,当使用 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
p
因此,由p2
和创建的超时p3
都会在这些变量初始化后立即启动。由于它们都将在 5 秒后超时,因此相应的 Promise (p
和p2
)p3
将在大约同一时间得到解决。同样,这与您是否使用await
,then
或Promise.all
只是忘记那些承诺并且不对它们做任何事情无关。相比之下,函数
pp
只是一个函数。这不是一个承诺。只有当您实际调用该函数时,您才会创建一个带有相应超时的承诺。await
使async
函数立即返回,返回一个承诺。这意味着函数的其余代码被推迟,而不是其他 Javascript 代码的其余部分:函数返回并在该函数调用后继续执行。当调用堆栈为空时,将处理不同的事件队列。所以它没有阻塞。一旦传递给 resolve 的 promise 被await
解析,神奇的事情就会发生:async
的函数执行上下文被恢复并且执行恢复到下一个await
。如果没有更多await
的执行,并且函数到达其末尾,它返回的承诺(当它遇到第一个时await
)被解决。Promise.all
不影响个人承诺何时解决。它创建一个新的 Promise,当所有给定的 Promise 都已解决时,该 Promise 将解决。如上所述,您的案例 (p
,p2
) 中的承诺大约在同一时刻Promise.all
解决,因此也将在大约 5 秒后解决。如果你这样做
await pp()
了,那么你只会在那时而不是更早地创建那个承诺。而且因为你有一个await p
before 它,它需要 5 秒才能pp()
执行。所以相应的超时直到这一秒await
被执行后才开始。这就是为什么await p; await pp()
需要两次 5 秒才能解决的原因。当你这样做时,情况就不同了
await p; await p2
。已经创建了两个 Promise,并且它们的超时时间已经开始。因此,当第一个结束 5 秒后await
,JS 将到达第二个await
并且会发现它p2
也已解决。不会有任何额外的等待时间(除了异步管理,它await
总是将一个事件放在微任务队列中)
我认为通过这些元素,您可以正确地描绘代码将如何执行,以及您获得的输出如何符合预期。
推荐阅读
- amazon-web-services - DynamoDB PutItem 不创建新项目
- google-container-registry - 如何在 Google Artifact Registry 中的项目之间复制工件
- python - 为什么sqlalchemy使用DeclarativeMeta类继承将对象映射到表
- email - 电子邮件协议、邮件服务器更改并使用现有托管中的其他协议
- jupyter-notebook - GCP 笔记本实例:错误 524 无法打开 Jupyter 实验室
- ffmpeg - FFMPEG 结合过滤器用于水印和视频速度
- python - 无法在php中反序列化redis缓存数据
- vue.js - Vue中无参数时处理404页面
- spring-boot - Spring boot Heroku 编译:无效的目标版本:11
- php - 读取 VBO 自定义操作 php 文件中的视图字段值