关于javascript:同步代码中dom操作不生效的问题

7次阅读

共计 1229 个字符,预计需要花费 4 分钟才能阅读完成。

很多时候会遇到这种状况,因为某个执行过程很耗时间,所以想在执行前加上 loading,执行后把 loading 去掉。
例如如下代码:

document.body.innerText = "loading";    //dom 操作 1
var now = new Date().valueOf()
while(new Date().valueOf() - now <= 2000) {
  //do something
    continue;
}
document.body.innerText = "done";        //dom 操作 2 

看代码目标执行成果应该是页面上显示 loading,2s 后变成 done, 但大家能够试一下,执行进去后果应该是页面 2s 之后间接变成 done,并没有进去 loading。

之所以呈现这样的景象是因为浏览器内核是多线程的,代码执行和 dom 渲染别离由 javascript 引擎线程和 GUI 渲染线程负责,且 js 线程与 gui 渲染线程是互斥的,当 js 线程执行时,渲染线程呈挂起状态,只有当 js 线程闲暇时渲染线程才会执行。
而 javascript 是单线程执行的,只有当调用栈中的所有同步工作执行结束,线程才会闲暇进去,去调用 GUI 渲染,执行 dom 的操作。

基于以上的原理,再看下面的代码,js 会在所有的同步代码执行完,才会渲染 dom,所以用户看到的就是 js 执行了 2s, 页面间接呈现 done

如果真的有业务要实现这样的成果怎么办呢?
1、应用异步操作(或 setTimeout 等),把要执行的 dom 操作脱离以后调用栈,放到工作队列里。这样就会在调用栈中的代码执行结束,线程闲暇进去,执行一次 dom 操作后,在执行工作队列中的工作,从而执行第二次 dom 操作。如下代码

document.body.innerText = "loading";    //dom 操作 1
setTimeout(function(){
  //do something
  document.body.innerText = "done";     //dom 操作 2
},2000)

2、网上还有另一种办法,应用 alert 语句会阻塞 js 线程,故将执行权让给 gui 渲染线程,所以之前的 dom 的操作都进行体现。如下代码

document.body.innerText = "loading";    //dom 操作 1
alert("doing")
document.body.innerText = "done";       //dom 操作 2 

但亲测这种办法只在火狐中好使,在 chrome 和 Edge 中都没有进去 loading。而且间接弹框 alert 用户体验也不太好,所以这种办法实际意义和成果都不举荐。

看到这儿了再深刻一下 javascript 的运行机制

咱们都晓得工作执行有同步执行和异步执行。实际上失常的同步工作都会在主线程上执行,造成一个“执行栈”。而除了这个“执行栈”,还会在主线程外有一个“工作队列”。只有异步工作有了运行后果,就会在工作队列中搁置一个事件,当“执行栈”所有的同步工作实现之后,零碎会读取“工作队列”,放入“执行栈”,开始执行。

正文完
 0