这是一个基本问题,但我在任何地方都找不到答案。
我们有两种方法:
// 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
这意味着someFunction2
在someFunction1
之前完成。两者是平行的。
很容易看出区别
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
。 第二个几乎在同一时刻开始3
和4
。
如果您从一个模拟执行某些工作的函数开始,该函数在该工作的各个阶段输出但需要一些时间。
例如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 的函数并恢复该调用方的执行来继续执行。
因此,每个等待都会在恢复之前暂停执行。无需演示。