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

document.body.innerText = "loading";    //dom操作1var 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操作1setTimeout(function(){  //do something  document.body.innerText = "done";     //dom操作2},2000)

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

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

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

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

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