TL; 博士
不要在你得到promise的问题中使用模式,然后单独等待它们;相反,使用Promise.all
(至少现在):
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
虽然您的解决方案确实并行运行这两个操作,但如果两个Promise都拒绝,它就无法正确处理拒绝。
细节:
您的解决方案并行运行它们,但始终等待第一个完成,然后再等待第二个。如果你只是想启动它们,并行运行它们,并获得两个结果,那很好。 (不,不是,请继续阅读...)请注意,如果第一个需要(例如)五秒钟才能完成,而第二个在一秒钟内失败,则您的代码将等待整整五秒钟,然后才会失败。
可悲的是,目前await
没有进行并行等待的语法,所以你有你列出的尴尬,或者Promise.all
. (不过,已经讨论过await.all
或 类似的;也许有一天。)
该Promise.all
版本是:
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
...更简洁,如果第二个操作快速失败,也不会等待第一个操作完成(例如,在我上面的五秒/一秒示例中,上面将在一秒内拒绝而不是等待五秒) . 另请注意,对于您的原始代码,如果第二个Promise在第一个Promise解决之前拒绝,您很可能会在控制台中收到“未处理的拒绝”错误(您目前使用 Chrome v61;更新:更新的版本有更有趣的行为)虽然这个错误可以说是虚假的(因为你这样做,最终处理的拒绝,在此代码显然是在async
function¹,因此该功能将钩拒绝做出Promise拒绝使用它)(更新:再次,改变了)。但是如果两个Promise都拒绝,你会得到一个真正的未处理的拒绝错误,因为控制流永远不会到达const value2 = await p2;
,因此 p2 拒绝永远不会被处理。
未处理的拒绝是一件坏事™(以至于很快,Node.js 将中止真正未处理的拒绝的过程,就像未处理的异常一样——因为它们就是这样),所以最好避免“得到Promise然后await
它”你的问题中的模式。
这是失败情况下时间差异的示例(使用 500 毫秒和 100 毫秒,而不是 5 秒和 1 秒),并且可能还有可以说是虚假的未处理拒绝错误(打开真实浏览器控制台查看):
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, "value1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
在这里,我们拒绝p1
和p2
,导致 上的非虚假未处理拒绝错误p2
:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
在您问过的评论中:
附带问题:以下强制是否会同时等待(并丢弃结果)await p1 && await p2
?
这与原始代码在Promise拒绝方面存在相同的问题:p1
即使p2
更早拒绝,它也会等到解决;如果在解决之前拒绝,它可能会产生一个可以说是虚假的(更新: 或临时)未处理的拒绝错误;和它产生如果两个真正未处理拒绝错误和拒绝(因为的拒绝从未处理)。p2
p1
p1
p2
p2
这是p1
解决和p2
拒绝的情况:
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, false);
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
...并且两者都拒绝:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
¹ “......这段代码显然在一个async
函数中......”在 2017 年编写此问题和答案时确实如此。从那时起,顶层await
发生/正在发生。