JavaScript中async函数用await与不用await的差异及代码等价性问询
在async函数中使用await与不使用await的区别
先给你说最核心的点:await就是async函数里用来「暂停执行、等Promise跑完」的关键,不用它的话,Promise就只是个普通的异步任务,根本不会拦住async函数的同步流程。具体可以拆成这几个细节:
执行流程是否阻塞:
- 不用await:async函数里的Promise会被立刻创建并丢进异步队列,但函数本身该往下走就往下走,完全不等Promise完成。比如你写:
这里async function test() { let p = Promise.resolve('hello'); console.log('after creating promise'); } test();after creating promise会在Promise还没resolve的时候就打印出来,根本不会等。 - 用await:碰到
await p时,async函数会立刻暂停,把执行权还给调用栈,直到这个Promise变成resolved状态,才会接着执行后面的代码,而且await会自动把Promise的resolve结果取出来。上面的例子改成await p; console.log('after creating promise'),那这句话就得等Promise resolve完才会打印。
- 不用await:async函数里的Promise会被立刻创建并丢进异步队列,但函数本身该往下走就往下走,完全不等Promise完成。比如你写:
返回值怎么处理:
- 不用await:如果你在async函数里返回一个Promise,函数会直接把这个Promise当自己的返回值,但要是只是创建Promise却不await,那这个Promise的结果跟函数后续代码一点关系都没有。
- 用await:你可以直接把await的结果赋值给变量,比如
let res = await Promise.resolve(1),res拿到的就是1,不是那个Promise对象,省得你再写.then()取结果。
错误怎么捕获:
- 不用await:如果Promise rejected了,你得用
.catch()去抓错误,不然就会变成未捕获的Promise报错。 - 用await:直接把await放进try/catch块里就行,写法跟同步代码抓错误一模一样,比如:
async function test() { try { await Promise.reject('出错了'); } catch (err) { console.log(err); // 会打印'出错了' } }
- 不用await:如果Promise rejected了,你得用
两段代码的等价性分析
现在来对比你给的两段代码,咱们从执行时间、输出结果、同步/异步逻辑三个方面说清楚:
代码1:
(async () => { let i = 100000; while(i--); })();
代码2:
(async () => { let promise = new Promise((resolve, reject) => { let i = 100000; while(i--); return resolve(); }); await promise; })();
1. 执行时间是否相同?
几乎一样,但代码2会多一丢丢可以忽略的开销。因为代码1里的while循环是直接在async函数的同步阶段跑完的;代码2里的while循环是在Promise的executor函数里执行的,而executor函数是创建Promise时就同步执行的,所以while循环的执行时间和代码1完全一致。但代码2多了创建Promise对象、处理await的异步调度开销,不过这个开销极小,几乎感觉不到。
2. console.log输出结果是否一致?
如果两段代码都没写console.log,那输出当然都是空的。但要是我们在两段代码末尾加个console.log('done'),再在外面加个console.log('start')和console.log('end'),差别就出来了:
- 代码1的输出顺序是:
start→done→end。因为代码1里没有await,整个async函数是同步执行的,while循环跑完就立刻打印done,然后才会执行外面的end。 - 代码2的输出顺序是:
start→end→done。因为await会把后面的代码放到微任务队列里,得等当前所有同步任务(包括外面的end)都跑完,才会从微任务队列里取出代码打印done。
所以只要有console.log,输出结果的顺序是完全不一样的!
3. 同步/异步执行逻辑是否相同?
完全不同:
- 代码1:整个async函数是纯同步执行的。虽然它是async函数,但里面没有任何异步操作或者await,所以它跟普通函数没区别,同步跑完所有代码,最后返回一个resolved的Promise(async函数默认返回resolved的Promise,除非你自己返回了其他Promise)。
- 代码2:虽然Promise的executor是同步执行的,但
await promise触发了async函数的暂停,把后续代码(如果有的话)丢进了微任务队列。它的执行流程是:- 同步创建Promise,立刻执行executor里的while循环(同步跑完)。
- 调用resolve(),Promise变成resolved状态。
- 碰到await,async函数暂停,把执行权还给调用栈。
- 等当前所有同步任务都执行完,再从微任务队列里取出await后面的代码继续执行。
简单说,代码1就是个披着async外衣的同步函数,代码2则是真正包含了异步调度逻辑的函数——哪怕while循环是同步的,但await把整个函数的后半段变成了微任务。
内容的提问来源于stack exchange,提问作者Michael Hsu
相关产品推荐
相关产品推荐

