同步与异步函数执行时长困惑:为何异步计算函数的计时结果不符合预期?
嘿,这个问题正好戳中了很多人对Promise的常见误区——咱们一步步把它掰明白:
核心误区:Promise构造函数的执行器是同步执行的!
很多人以为只要把代码放进Promise里,它就会“自动异步跑”,但事实是:你传给new Promise()的那个执行器函数(就是包含resolve的那个函数),是在调用Promise的瞬间同步执行的!
咱们结合你的代码具体分析:
1. 先看你的asyncCalc为什么计时14ms
当你调用asyncCalc()的时候,JS引擎会立刻执行Promise执行器里的所有代码:也就是那个1000万次的for循环,会同步跑完,等循环结束后才会调用resolve(v),然后Promise对象才会被返回给你。
所以你写的这段代码:
console.time('asyncCalc'); asyncCalc().then(v => console.log("Result:", v)); console.timeEnd('asyncCalc');
从console.time到console.timeEnd的过程中,已经把整个for循环都执行完了!计时的14ms,其实就是这个循环的纯执行时间。
那为什么比syncCalc的27ms短?因为syncCalc的计时里还包含了console.log("Result:", syncCalc())的开销——你同步调用syncCalc()拿到结果后,立刻执行console.log,这个log的耗时被算进了syncCalc的计时里;而asyncCalc的console.log是在then回调里,属于微任务,要等当前同步代码块(包括console.timeEnd)全部执行完才会触发,所以asyncCalc的计时没包含log的开销,自然就少了一截。
2. 再看asyncTimeout为什么只有0.9ms
这个例子里,Promise执行器里只做了一件事:调用setTimeout。setTimeout是Node的异步API,调用它的时候,JS引擎只是把回调函数(() => resolve(...))放到了定时器任务队列里,然后就立刻结束了执行器函数——也就是说,asyncTimeout()几乎瞬间就返回了Promise对象。
这时候从console.time到console.timeEnd的时间,只是调用asyncTimeout、创建Promise、注册定时器这几步的微小开销,所以才会只有不到1ms。
给你做个小验证
你可以给asyncCalc加一行同步日志:
const asyncCalc = () => new Promise(resolve => { console.log("我是同步执行的循环开始啦!"); let v = 0; for (let i = 1; i < 10000000; i++) { v += i } resolve(v); })
运行后你会发现,"我是同步执行的循环开始啦!"会在asyncCalc: 14.152ms之前打印出来,这就实锤了循环是同步执行的。
最后总结几个关键点
- Promise本身不是“异步代码的容器”,它只是一个异步结果的占位符
- 只有当你在Promise执行器里调用了真正的异步API(比如
setTimeout、文件IO、网络请求等),才会让后续逻辑进入异步队列 - 如果执行器里全是同步代码,那整个Promise的创建过程就是同步的,和普通函数的区别只是最后返回了一个Promise对象而已
备注:内容来源于stack exchange,提问作者codemonkey

