等待/异步如何处理未解决的承诺
Posted
技术标签:
【中文标题】等待/异步如何处理未解决的承诺【英文标题】:await/async how to handle unresolved promises 【发布时间】:2020-04-21 17:18:53 【问题描述】:你如何处理无法解决的承诺?
例子:
class Utils
static async thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(number: number)
return new Promise((resolve, reject) =>
if(number === 2)
resolve('ok')
)
console.log(await Utils.thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(2))
// this will print "ok" because 2 is passed and the promise is resolved
console.log(await Utils.thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(5))
// this will crash the program silently
uncaughtException
和 unhandledRejection
在 promise 未解决时不返回任何内容。在 await 周围添加 try/catch
不起作用(没有错误)。最后,唯一可行的是使用Promise.then
而不是await
。
问题是代码库充斥着async/await
和有时会解决的承诺(取决于条件)
问题:我可以添加一个打字稿标志来检测丢失的解析/拒绝吗?或者可能是一种将所有async/await
转换为使用Promise.then
的自动方式?
使用调试器时,程序在 Promise 之后停止,很难找到哪个函数/promise 缺少解析/拒绝。
重写所有async/await
调用以使用Promise.then
是我最后的手段。
【问题讨论】:
问题是:代码库中充斥着 async/await 和有时会解决的 Promises 这是代码库需要解决的问题。虽然有一些方法可以绕过它(例如,等待该 Promise 和 60 秒后拒绝的 Promise 的 Promise.race),但您不应该在消费者中这样做 - 修复为您提供的功能相反,这些 Promise 基本上都被打破了 return new Promise((resolve, reject) => if(number === 2) resolve('ok') else resolve('not ok') )跨度> @CertainPerformance 你知道发生这种情况时我如何获得堆栈跟踪或错误吗?让我生气的是,这一切都在悄无声息地发生。我们的错误日志是空的,因为节点没有报告任何内容。我正在考虑包装整个程序并返回退出代码以检测它。如上所述,代码库中到处都是它,我需要在重构所有这些承诺之前确定问题的大小。谢谢! 对于一般情况,不可能从外部知道 Promise 是永远挂起还是最终会解决(或拒绝)。修复基础代码使其不会从根本上被破坏应该是第一步,而不是第二步 【参考方案1】:如果您的承诺偶尔不会解决或拒绝,并且这不是它们应该工作的方式(通常不是),那么您只需要解决它。真的没有变通办法。正确的解决方法是降低到最低级别并修复代码,以便每次都能可靠地解析或拒绝。
这不是正确的解决方法,但实现超时包装器可以帮助调试,为您提供一条日志消息,其中包含一些类似堆栈跟踪的超时承诺:
function rejectT(t)
// create potential error here for better opportunity at stack trace
let e = new Error("Promise timed out");
return new Promise((resolve, reject) =>
setTimeout(() =>
console.log(e);
reject(e);
, t);
);
function timeout(p, t = 5000)
return Promise.race([p, rejectT(t)]);
然后您可以包装任何承诺,而不是:
fn().then(...).catch(...)
你可以使用:
timeout(fn()).then(...).catch(...);
或者,如果您想设置自定义超时值:
timeout(fn(), 1000).then(...).catch(...);
再次强调,这是调试代码,以帮助找到需要修复的罪魁祸首并帮助测试修复,而不是对您的代码进行创可贴。
重写所有 async/await 调用以使用 Promise.then 是我最后的选择。
我完全看不出这有什么帮助。如果await
永远不会完成,promise.then()
也不会。在这方面它们完全相同。如果承诺永远不会解决或拒绝,那么.then()
处理程序也永远不会被调用。
问题是代码库充满了异步/等待和有时会解决的承诺(取决于条件)
除了有条不紊地进行代码审查之外,这里没有捷径可走,可以找到具有可能永远无法解析或拒绝的代码路径的可疑代码,然后构建单元测试来测试在各种条件下返回承诺的每个函数。
从不解析或拒绝的一个可能的代码来源是一些 Promise 反模式。其中一些是反模式的确切原因是因为它们很容易搞砸。以下是一些可能会提高您对可疑代码敏感度的参考资料:
Promise Anti-Patterns
Common Promise Anti-Patterns and How to Avoid Them
ES6 Promises: Patterns and Anti-Patterns
【讨论】:
我已经在使用 tslint 并且开启了“promise”规则;你知道是否有另一个 linter 能够为我检测这些反模式?到目前为止,我正在考虑记录请求和一些退出代码;没有退出代码的表示程序意外终止 @hbt - 我不知道检测承诺反模式的 linter。在已知函数返回承诺的情况下,使用 TypeScript 会更可行。 仅供参考:找到了一种方法来检测使用异步钩子nodejs.org/api/async_hooks.html 无法解析/拒绝的承诺——我可以识别哪些请求产生未解决的承诺然后修复它们。我使用 codemod 在 await 调用周围添加钩子,然后运行测试套件和看起来有问题的旧请求,以查找哪些承诺被破坏。谢谢! @hbt - 很酷。我猜是 node.js 中的一些相对较新的东西。【参考方案2】:async function thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(number)
return new Promise((resolve, reject) =>
if (number === 2)
resolve('ok')
else
reject('error:' + number)
)
(async() =>
try
console.log(await thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(2))
// this will print "ok" because 2 is passed and the promise is resolved
catch (e)
console.error(e);
try
console.log(await thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(5))
// this will crash the program silently
catch (e)
console.error(e);
)()
【讨论】:
这不是真正的问题。 OP 只是展示了一个不可靠的示例承诺。真正的问题在于 OP 可能没有编写的一些其他代码,但正在尝试修复使用这些不可靠承诺的代码。以上是关于等待/异步如何处理未解决的承诺的主要内容,如果未能解决你的问题,请参考以下文章