问题描述
在使用 ES8 的 async/await 进行异步操作时,有时可能会出现以下错误:
Uncaught TypeError: Cannot read property 'then' of undefined
这个错误的出现通常是因为 await 对象没有返回 Promise 对象,导致无法正确执行 then/catch 等 Promise API 方法。
原因分析
通常情况下,await 后面跟的是一个 Promise 对象,例如:
async function example() {
const result = await someAsyncFunction()
console.log(result)
}在这个例子中,someAsyncFunction() 必须返回一个 Promise 对象,否则会导致上面的错误。
但实际上,有些情况下,async 函数可能会在 await 语句中返回一个普通类型的值,例如:
async function example() {
const result = await someSyncFunction()
console.log(result)
}这里的 someSyncFunction() 是一个同步函数,并不返回 Promise 对象,但是在这里却可以使用 await 等待其执行完成,并且返回一个值。
在这种情况下,await 会把这个值包装成一个 Promise 对象并返回,但是它并不会设置该 Promise 对象的状态为 resolved 或者 rejected,导致无法正确执行 then/catch 等 Promise API 方法,从而抛出错误。
解决方法
为了解决这个问题,我们需要手动将非 Promise 对象包装成一个 Promise 对象,并设置其状态为 resolved 或者 rejected。最简单的方法是使用 Promise.resolve() 和 Promise.reject() 这两个静态方法。
例如,如果我们要使用 await 等待一个可能返回普通类型的值的函数,可以这样写:
// www.javascriptcn.com code example
async function example() {
let result = null
try {
result = await Promise.resolve(someSyncFunction())
} catch (error) {
result = Promise.reject(error)
}
console.log(result)
}上面的代码中,首先用 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 返回普通类型值的问题:
// www.javascriptcn.com code example
async function example() {
let result = null
try {
result = await Promise.resolve(someSyncFunction())
} catch (error) {
result = Promise.reject(error)
}
console.log(result)
}
function someSyncFunction() {
console.log("someSyncFunction called")
return "Hello, world!"
}
example()通过运行上面的代码,我们可以看到控制台输出了“Hello, world!”,证明我们成功地解决了 Uncaught TypeError: Cannot read property 'then' of undefined 的错误。
指导意义
ES8 的 async/await 是一种方便且易用的异步编程方式,但是在使用它时需要注意一些细节,特别是对于 await 后面可能返回普通类型值的情况,需要手动将其包装成 Promise 对象。这个问题可能并不容易被发现,但是一旦出现就会导致一系列的问题,因此需要特别关注。
在实际开发中,我们可以使用 TypeScript 等静态类型检查工具来辅助检测这个问题,避免出现类似的错误。此外,我们也可以编写一些通用的函数,帮助我们包装非 Promise 对象,减少代码冗余。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67cffa70e46428fe9ec5dd2e