为何JavaScript中一个Promise函数非阻塞、另一个阻塞代码?Promise.resolve与new Promise的行为差异解惑
为什么
nonBlockingCode看起来不阻塞,而blockingCode会阻塞后续代码? 先来看你提供的两段代码:
function blockingCode() { return new Promise((resolve, reject) => { for (let i = 0; i < 2500000000; i++) { // Doing nothing... } resolve('ping'); }); } function nonBlockingCode() { return Promise.resolve().then(() => { for (let i = 0; i < 2500000000; i++) { // Doing nothing... } return 'pong'; }); } // 测试非阻塞代码 console.time('non-blocking'); nonBlockingCode().then((message) => console.log(message)); console.timeEnd('non-blocking'); // 测试阻塞代码(注释状态) // console.time('blocking'); // blockingCode().then((message) => console.log(message)); // console.timeEnd('blocking');
接下来我会拆解两者的执行逻辑差异,核心在于JavaScript事件循环的同步任务和微任务执行顺序:
1. blockingCode为啥会阻塞?
当你调用blockingCode()时,发生了这些事:
new Promise()的构造函数会立刻同步执行你传入的executor回调(就是那个带resolve/reject的函数)- 紧接着那个25亿次的for循环就开始跑了——JavaScript是单线程的,同步任务没执行完,主线程根本腾不出手处理后面的代码
- 等循环终于跑完,才会调用
resolve('ping'),把Promise标记为完成状态 - 这时候,后面的
console.timeEnd('blocking')才有机会执行
所以实际运行时,调用blockingCode()后,整个程序会卡住,直到循环结束,才会输出时间和ping——完全阻塞了后续同步代码。
2. nonBlockingCode为啥看起来不阻塞?
调用nonBlockingCode()时,流程是这样的:
Promise.resolve()会立刻创建一个已完成的Promise,但它的.then()回调不会马上执行——这个回调会被塞进微任务队列,等待当前所有同步任务执行完毕后才会被处理- 所以
nonBlockingCode()本身很快就执行完了(只是返回一个Promise,把回调放进队列),主线程立刻转向执行后面的console.timeEnd('non-blocking'),先输出计时结果 - 等当前所有同步代码(包括
console.time、函数调用、console.timeEnd)都跑完,事件循环才会去处理微任务队列里的那个.then()回调,这时候才会开始跑25亿次的for循环 - 循环结束后返回
'pong',触发后续的.then()输出消息
这里要澄清:这个循环本身还是会阻塞主线程的,只是它的执行被推迟到了当前同步代码之后,所以看起来没有阻塞console.timeEnd的执行而已。
3. 解答你的核心疑惑:Promise.resolve和new Promise的差异?
你说的没错,两者最终都会生成一个已完成的Promise,但差异在于代码的执行时机:
new Promise(executor)里的executor函数是同步执行的,所以里面的大循环会立刻抢占主线程Promise.resolve().then(callback)里的callback是微任务,会被放到队列排队,等当前同步任务全部结束才会执行
这下应该能理清逻辑了吧?
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

