解决 ES8 async/await 报错 Uncaught TypeError: Cannot read property 'then' of undefined 的问题

· 约 4 分钟读完

问题描述

在使用 ES8 的 async/await 进行异步操作时,有时可能会出现以下错误:

Uncaught TypeError: Cannot read property 'then' of undefined

这个错误的出现通常是因为 await 对象没有返回 Promise 对象,导致无法正确执行 then/catch 等 Promise API 方法。

原因分析

通常情况下,await 后面跟的是一个 Promise 对象,例如:

在这个例子中,someAsyncFunction() 必须返回一个 Promise 对象,否则会导致上面的错误。

但实际上,有些情况下,async 函数可能会在 await 语句中返回一个普通类型的值,例如:

这里的 someSyncFunction() 是一个同步函数,并不返回 Promise 对象,但是在这里却可以使用 await 等待其执行完成,并且返回一个值。

在这种情况下,await 会把这个值包装成一个 Promise 对象并返回,但是它并不会设置该 Promise 对象的状态为 resolved 或者 rejected,导致无法正确执行 then/catch 等 Promise API 方法,从而抛出错误。

解决方法

为了解决这个问题,我们需要手动将非 Promise 对象包装成一个 Promise 对象,并设置其状态为 resolved 或者 rejected。最简单的方法是使用 Promise.resolve() 和 Promise.reject() 这两个静态方法。

例如,如果我们要使用 await 等待一个可能返回普通类型的值的函数,可以这样写:

上面的代码中,首先用 let 声明了 result 变量,并初始化为 null。然后,使用 try...catch...finally 语句块,将 await 包装在 try 中,并使用 Promise.resolve() 将 someSyncFunction() 的返回值包装成一个 Promise 对象。如果 someSyncFunction() 抛出了错误,则使用 Promise.reject() 将这个错误包装成一个 Promise 对象,最后将 result 打印出来。

通过这样的方式,我们就可以确保 await 后面一定是一个 Promise 对象,从而避免 Uncaught TypeError: Cannot read property 'then' of undefined 的错误。

示例代码

下面是一个完整的示例代码,演示了如何使用 Promise.resolve() 和 Promise.reject() 解决 await 返回普通类型值的问题:

通过运行上面的代码,我们可以看到控制台输出了“Hello, world!”,证明我们成功地解决了 Uncaught TypeError: Cannot read property 'then' of undefined 的错误。

指导意义

ES8 的 async/await 是一种方便且易用的异步编程方式,但是在使用它时需要注意一些细节,特别是对于 await 后面可能返回普通类型值的情况,需要手动将其包装成 Promise 对象。这个问题可能并不容易被发现,但是一旦出现就会导致一系列的问题,因此需要特别关注。

在实际开发中,我们可以使用 TypeScript 等静态类型检查工具来辅助检测这个问题,避免出现类似的错误。此外,我们也可以编写一些通用的函数,帮助我们包装非 Promise 对象,减少代码冗余。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67cffa70e46428fe9ec5dd2e