为何函数执行初期无法立即修改按钮文本?(Chrome环境)
问题分析与解决方案
你的问题核心在于JavaScript的单线程执行模型——浏览器的UI渲染和JavaScript代码运行共享同一个主线程。当你在函数里同步执行那个2秒的while循环时,整个线程被完全阻塞了:虽然你已经修改了按钮的DOM内容,但浏览器根本没时间去执行渲染更新,必须等当前函数的所有代码跑完,主线程空闲下来,才会处理渲染队列里的任务。这就是为什么元素面板里能看到文本变了,但界面要等函数结束才显示。
解决办法:把耗时操作异步化
我们可以用setTimeout把耗时的阻塞代码放到下一个事件循环里,这样当前函数执行完(修改DOM的代码先完成),主线程空闲后,浏览器会先完成按钮文本的渲染,再执行后续的耗时操作。
修改后的代码如下:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script> <button class="btn btn-primary" onclick="setToDisabled(this)">Toggle</button> <script type="text/javascript"> function setToDisabled(btn) { console.log('start'); // 先修改按钮文本,此时DOM已更新 $(btn).html("TEXT CHANGED ON START OF FUNCTION"); // 把耗时操作放到setTimeout里,让浏览器先完成渲染 setTimeout(function() { var d1 = new Date(); var t1 = d1.getTime(); var keeprunning = true; while(keeprunning) { var d2 = new Date(); var t2 = d2.getTime(); var dif = t2 - t1; if((dif / 1000) > 2) keeprunning = false; } console.log('done'); }, 0); // 延迟0毫秒,只是把任务放到下一个事件循环 } </script>
补充说明
- 这里的
setTimeout(..., 0)并不是真的等0毫秒,而是告诉浏览器:"当前JS执行栈清空后,再执行这个回调函数"。这样就给了浏览器足够的时间去处理渲染更新。 - 如果你的实际耗时操作是异步任务(比如AJAX请求),那本身就不会阻塞线程,直接写就行;但如果是同步的计算密集型操作,就需要用这种异步化的方式来避免阻塞UI。
内容的提问来源于stack exchange,提问作者Marcel
相关产品推荐
相关产品推荐

