async 函数一定返回 Promise 吗?await 后面不是 Promise 会怎样?
先理解原理和示例,再练习完整口述与追问。
回答要点
async 函数的返回值一定是 Promise。函数里 return 普通值 等价于这个 Promise 兑现为该值;return thenable 会再解包一层;抛错则拒绝。await 对非 Promise 也会先 Promise.resolve,恢复至少延后一个微任务。await 到拒绝时,异常在 await 表达式处抛出。
原理拆解
调用 async 函数时同步执行到第一个 await,这之前的代码和普通函数一样立刻发生。遇到 await 后,函数暂停,已完成的部分包进返回的 Promise。之后每段恢复都是微任务,所以 await 1 也不会让后面的代码留在当前同步任务里。
return 1 使返回的 Promise 兑现为 1。return Promise.resolve(2) 或返回其他 thenable 时,不会得到“Promise 套 Promise”,而会采纳内层结果,最终兑现为 2。throw 和 return Promise.reject(...) 都会让返回的 Promise 拒绝。在 await 一个拒绝的 Promise 时,错误变成 await 所在位置的异常,可以被函数内的 try...catch 接住;没接住就拒绝调用方拿到的那个 Promise。
并行与串行取决于有没有先把 Promise 创建出来。await a(); await b() 是串行。const pendingA = a(); const pendingB = b(); await pendingA; await pendingB 才是同时开始、按顺序取结果。
示例与推演
async function first() {
console.log('a')
await 1
console.log('b')
return 2
}
first().then(value => console.log(value))
console.log('c')
// 同步:a、c;微任务:b、2
await 1 没有真正的异步 I/O,仍然把 b 推迟到同步代码和当时已排队的微任务之后。若把 return 2 改成 return Promise.resolve(2),then 拿到的仍是 2,不是 Promise。
易错点
forEach(async item => await save(item)) 不会等待,见宝典 4。async 函数外面如果不 await 或 .catch,拒绝会变成未处理拒绝。不要以为 await 非 Promise 可以当作同步写完。
参考回答可直接练习口述
async 函数无论 return 什么,调用结果都是 Promise。return 普通值就兑现为该值,return 一个 Promise 会解包成它的结果,throw 则拒绝。await 后面不是 Promise 时,会先包成 Promise.resolve,所以后面的代码至少要等一个微任务,不能继续留在当前同步流程里。await 到拒绝时,错误就在这一行抛出,函数内的 try...catch 能接住。要并行就先把两个调用都发起,再分别 await。