我正在尝试思考一种实践,我现在注意到在不同的代码库中,函数未解决补充异步函数调用。
我认为这样做是为了潜在地节省性能,因为等待承诺得到解决可能会增加额外的开销,而除了保证一切都已完成之外,不会获得真正的好处(如果承诺对函数的结果不重要)。完成了。
我见过的一个常见示例是缓存:
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
将立即执行,但尚未解析(除非您不执行 any 异步工作并立即返回)。举例说明:
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 承诺会变成拒绝。即使我将其包围在 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
最终完成)。
但是,是的,即使您将
await someFunction()
包装到 try {...} catch {...}
块中,您的代码也会抛出未处理的承诺拒绝,因为您丢失了 cacheResult
返回的承诺的上下文。即,在 someFunction()
返回的 Promise 解决或拒绝之前,cacheResult
中的异步代码将早已完成(并且它返回的 Promise 也将长期被解析(或可能被拒绝))。
所以,如果你不关心
cacheResults
是否成功,要么通过这样做来使其在任何情况下都不会抛出
async function cacheResult(foo) {
try {
//some async code
} catch {
}
}
或者在调用时添加拒绝处理程序
async function someFunction() {
//some code
cacheResult(...).catch(e => {})
//some more code
}
这两个变体都会让
cacheResult
稍后完成,并在失败时直接忽略错误。当然,您可以(并且可能应该)在这两种变体中添加一些日志记录......
但要注意一件事:例如,nodejs 可能不会等待待处理的 Promise 并终止,因此,如果没有其他任何事情可以让您的进程保持活动状态,您的
cacheResult
函数可能会随时终止...