Promise.all vs [await x, await y] - 真的一样吗?



这是一个基本问题,但我在任何地方都找不到答案。

我们有两种方法:

// consider someFunction1() and someFunction2() as functions that returns Promises
Approach #1:
return [await someFunction1(), await someFunction2()]
Approach #2:
return await Promise.all([someFunction1(), someFunction2()])

我的团队负责人说,这两种方法最终都采用相同的解决方案(两个功能并行执行)。但是,据我所知,第一种方法await someFunction1()解决,然后执行 someFunction2。

所以这就是问题所在,它真的是一样的,还是第二种方法有任何性能改进?非常欢迎证明!

不,你不应该接受:

return [await someFunction1(), await someFunction2()];

与以下相同:

return await Promise.all([someFunction1(), someFunction2()]);

我还应该指出,不需要上述return await中的await。查看此博客文章以了解更多信息。

他们是不同的


第一种方法(顺序)

让我们通过检查这两种替代方法中的每一个的工作原理来确定差异。

[await someFunction1(), await someFunction2()];

在这里,在async上下文中,我们创建一个数组文字。请注意,调用someFunction1(每次调用时可能返回新承诺的函数)。

因此,当您调用someFunction1时,会返回一个新的 promise,然后"锁定"async上下文,因为前面的await

简而言之,await someFunction1()"阻止"数组初始化,直到返回的承诺得到解决(通过解析或拒绝)。

重复相同的过程以someFunction2

请注意,在第一种方法中,按顺序等待两个承诺。因此,与使用Promise.all的方法没有相似之处。让我们看看为什么。

第二种方法(非顺序)

Promise.all([someFunction1(), someFunction2()])

当你应用Promise.all时,它期望一个可迭代的承诺。它会等待您给出的所有承诺解析,然后再返回新的已解析值数组,但不要等到每个承诺解析后再等待另一个承诺解析。从本质上讲,它同时等待所有承诺,因此它是一种"非顺序"。由于 JavaScript 是单线程的,因此您无法分辨这种"并行",但在行为方面非常相似。

因此,当您传递此数组时:

[someFunction1(), someFunction2()]

您实际上是在传递一个承诺数组(从函数返回)。像这样:

[Promise<...>, Promise<...>]

请注意,承诺是在Promise.all之外创建的。

所以你实际上是在向Promise.all传递一系列承诺.当它们都得到解析时,Promise.all将返回解析值的数组。我不会详细解释Promise.all是如何工作的,为此,我建议您查看文档。

您可以通过在使用await之前创建承诺来复制这种"非顺序"方法。这样:

const promise1 = someFunction1();
const promise2 = someFunction2();
return [await promise1, await promise2];

在等待promise1时,promise2已经在运行(因为它是在第一个await之前创建的),因此行为类似于Promise.all

我的团队负责人说,这两种方法最终都采用相同的解决方案(两个函数并行执行)。

这是不正确的。

但是,据我所知,第一种方法await someFunction1()解决,然后执行 someFunction2。

这是正确的。

这是一个演示

方法1:

const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));

async function Approach1() {
return [await someFunction1(), await someFunction2()];
}

async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach1();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();

结果是:

hello
world
result: hello,world
total time: 1205

这意味着someFunction1首先运行到完成,然后执行someFunction2。它是顺序的

方法2:

const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));

async function Approach2() {
return await Promise.all([someFunction1(), someFunction2()]);
}

async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach2();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();

结果是:

world
hello
result: hello,world
total time: 803

这意味着someFunction2someFunction1之前完成。两者是平行的。

很容易看出区别

function createTimer(ms, id) {
console.log(`id: ${id} started ${new Date()}`);
return new Promise((res, rej) => {
setTimeout( () => {
console.log(`id: ${id} finished ${new Date()}`);
res(id);      
}, ms );
});
}
(async function() {
var result1 = [await createTimer(5000, '1'), await createTimer(5000, '2')];
var result2 = await Promise.all([createTimer(5000, '3'), createTimer(5000, '4')]);
console.log(result1);
console.log(result2);
})();

第一个开始1,当1完成时,它开始2。 第二个几乎在同一时刻开始34

如果您从一个模拟执行某些工作的函数开始,该函数在该工作的各个阶段输出但需要一些时间。

例如
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1); 
}
}, 1000);
});
}

然后你用第二种类似的方法复制它。你插入你的 2 个方法,你会看到使用Promise.all的方法并行执行,但使用 2 个await调用的方法串联执行。

平行

function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1); 
}
}, 1000);
});
}
function someFunction2(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction2", i);
if(i == 5){
clearInterval(intervalId)
resolve(2); 
}
}, 1000);
});
}
(async function(){
const result = await Promise.all([someFunction1(),someFunction2()]);
console.log("result",result);
})();

系列

function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1); 
}
}, 1000);
});
}
function someFunction2(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction2", i);
if(i == 5){
clearInterval(intervalId)
resolve(2); 
}
}, 1000);
});
}
(async function(){
const result = [await someFunction1(),await someFunction2()];
console.log("result",result);
})();

两者都给出了完全相同的结果,但到达那里是非常不同的。

MDN 文档Promise.all()指出

此方法可用于聚合多个承诺的结果。它通常用于整个代码依赖于多个相关的异步任务来成功工作 -我们希望在代码执行继续之前完成所有这些任务。

虽然它不是明确的,但您可以等待Promise.all跟踪多个承诺。只有当所有承诺都已解析时,代码执行才会继续。

您提到的在数组中捕获单独异步任务的另一种方法由于await的运行方式而有所不同。

await 拆分执行流,允许异步函数的调用方恢复执行。在 await 延迟异步函数的继续之后,后续语句的执行随之而来。如果此 await 是其函数执行的最后一个表达式,则通过向函数的调用方返回一个挂起的 Promise 以完成 await 的函数并恢复该调用方的执行来继续执行。

因此,每个等待都会在恢复之前暂停执行。无需演示。

相关内容

  • 没有找到相关文章

最新更新