不重要的承诺未得到解决是一种不好的做法吗?[复制]

Is it a bad practice to leave unimportant Promises unresolved? [duplicate]

提问人:Ceiling Gecko 提问时间:10/9/2023 更新时间:10/11/2023 访问量:128

问:

我正在尝试围绕一种做法,我现在注意到了不同的代码库,其中函数使补充异步函数调用未解决。

我认为这样做是为了节省性能,因为等待承诺被解决可能会增加额外的开销,除了保证一切都已经完成之外,没有获得任何真正的好处(如果承诺对函数的结果不重要)。

我见过的一个常见例子是缓存:

async function someFunction() {

  const result = await doSomething();
  
  cacheResult(result);
  
  return result;
}

async function cacheResult(someValue) {
  // ... cache logic returning a promise
}

我假设在上面的示例中,存在一种已完成之前已解决的情况是否正确?如果是这样,那么我认为这将“在以后的某个任意时间”解决?someFunctioncacheResultcacheResult

如果我被包裹在一个块中怎么办:someFunctiontry/catch

try {
  await someFunction();
} catch (error) {
  // ... error handling
}

但承诺在一段时间后变成了拒绝。这是否会导致未处理的承诺被拒绝并终止该过程,即使我将其包围在其中?cacheResulttry/catch

假设已经进行了适当的错误处理,并且它是一个需要相当长时间的复杂函数,那么让它自行解决并继续处理以免损失性能是否是一种有效的做法?cacheResult

JavaScript 节点.js 异步等待承诺

评论

0赞 Bergi 10/9/2023
你是在问永不解决承诺(以便它们保持待处理状态),还是询问永不兑现承诺(以便您忽略其结果)await
1赞 Bergi 10/9/2023
"这是否会导致未处理的承诺被拒绝并终止流程?-是的。所以不要那样做。在结束 promise 链时,请始终添加错误处理。

答:

3赞 RickN 10/9/2023 #1

我假设在上面的示例中,存在一种情况,即 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()(例如保存时间戳或修订号)。

2赞 derpirscher 10/9/2023 #2

是的,永远悬而未决的承诺是不好的做法,因为它们会占用资源。

但是在你的例子中没有(明显的)未解决的承诺。仅仅因为你没有承诺,这并不意味着它不会解决(假设最终完成)。是的,这完全没问题,如果你现在不关心他们的结果,就不要等待承诺。awaitcacheResult

但是,是的,即使您将代码包装到块中,您的代码也会抛出未经处理的 promise 拒绝,因为您丢失了 返回的 promise 的上下文。即,在返回的 promise 解析或拒绝之前,异步代码将长期完成(并且它返回的承诺将长期解析(或可能被拒绝))。await someFunction()try {...} catch {...}cacheResultsomeFunction()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

评论

0赞 Bergi 10/11/2023
"因为它们占用了资源“——实际上它们没有,如果它们的解析器函数是垃圾回收的并且它们本身没有被引用