提问人:Ceiling Gecko 提问时间:10/9/2023 更新时间:10/11/2023 访问量:128
不重要的承诺未得到解决是一种不好的做法吗?[复制]
Is it a bad practice to leave unimportant Promises unresolved? [duplicate]
问:
我正在尝试围绕一种做法,我现在注意到了不同的代码库,其中函数使补充异步函数调用未解决。
我认为这样做是为了节省性能,因为等待承诺被解决可能会增加额外的开销,除了保证一切都已经完成之外,没有获得任何真正的好处(如果承诺对函数的结果不重要)。
我见过的一个常见例子是缓存:
async function someFunction() {
const result = await doSomething();
cacheResult(result);
return result;
}
async function cacheResult(someValue) {
// ... cache logic returning a promise
}
我假设在上面的示例中,存在一种已完成之前已解决的情况是否正确?如果是这样,那么我认为这将“在以后的某个任意时间”解决?someFunction
cacheResult
cacheResult
如果我被包裹在一个块中怎么办:someFunction
try/catch
try {
await someFunction();
} catch (error) {
// ... error handling
}
但承诺在一段时间后变成了拒绝。这是否会导致未处理的承诺被拒绝并终止该过程,即使我将其包围在其中?cacheResult
try/catch
假设已经进行了适当的错误处理,并且它是一个需要相当长时间的复杂函数,那么让它自行解决并继续处理以免损失性能是否是一种有效的做法?cacheResult
答:
我假设在上面的示例中,存在一种情况,即 someFunction 在解析 cacheResult 之前已经完成,我是否正确?
事实上,这几乎是有保证的。 将立即执行,但尚未解决(除非您不执行任何异步工作并立即返回)。举例说明:cacheResult
async function doSomething() {
return 1;
}
async function someFunction() {
const result = await doSomething();
cacheResult(result).then(console.log);
return result;
}
async function cacheResult(someValue) {
await null; // have this function take at least one tick.
return 2;
}
someFunction().then(console.log);
// Logs 1, then 2.
如果是这样,那么我假设cacheResult将“在任意时间后”解析?
是的。
事实上,如果这样实现,它甚至根本不会解决:cacheResult
function cacheResult(someValue) {
return new Promise((resolve, reject) => {
// don't do anything with resolve() or reject()
});
}
但 cacheResult promise 会在一段时间后解析为拒绝。这是否会导致未处理的承诺被拒绝并终止该过程,即使我将其包围在 try/catch 中?
是的。这不会捕获调用中的异常,并导致未处理的承诺被拒绝:cacheResult
// Make it throw an error:
async function cacheResult(someValue) {
await null; // have this function take at least one tick.
throw new Error('Custom error');
}
// Run Node with warnings enabled:
// node --unhandled-rejections=warn example.js
// UnhandledPromiseRejectionWarning: Unhandled promise rejection.
// This error originated either by throwing inside of an async
// function without a catch block, or by rejecting a promise which
// was not handled with .catch(). (...)
事实上,错误消息指向了这个答案。
假设对 cacheResult 进行了适当的错误处理,并且它是一个需要相当长时间的复杂函数,那么让它自行解决并继续处理以免损失性能是否是一种有效的做法?
是的,这完全没问题:
async function someFunction() {
const result = await doSomething();
cacheResult(result).catch(err => console.warn("cacheResult:", err));
return result;
}
您可能需要考虑添加一种机制,以确保缓慢的 cacheResult()
调用不会覆盖恰好更快完成的较新的 cacheResult()(
例如保存时间戳或修订号)。
是的,永远悬而未决的承诺是不好的做法,因为它们会占用资源。
但是在你的例子中没有(明显的)未解决的承诺。仅仅因为你没有承诺,这并不意味着它不会解决(假设最终完成)。是的,这完全没问题,如果你现在不关心他们的结果,就不要等待承诺。await
cacheResult
但是,是的,即使您将代码包装到块中,您的代码也会抛出未经处理的 promise 拒绝,因为您丢失了 返回的 promise 的上下文。即,在返回的 promise 解析或拒绝之前,异步代码将长期完成(并且它返回的承诺将长期解析(或可能被拒绝))。await someFunction()
try {...} catch {...}
cacheResult
someFunction()
cacheResult
所以,如果你不想等待,或者你不在乎它是否成功,要么在任何情况下都不要扔cacheResult
async function cacheResult(foo) {
try {
//some async code
} catch {
//optional error handling/logging
}
}
或添加 rejectionhandler(在调用它时)
async function someFunction() {
//some code
cacheResult(...).catch(e => {
//optional error handling/logging
})
//some more code
}
这两种变体都会让完成时间稍晚一些,但仍然会捕捉到它抛出的任何错误。cacheResult
但要注意一件事:例如,nodejs 可能不会等待待处理的承诺并终止,因此如果没有其他事情可以使您的进程保持活动状态,您的函数可能会随时终止......cacheResult
评论
await