首先来看一段代码
//html部分<ulid="text"></ul>
//js部分vardivs=document.getElementById("text");setTimeout(()=>{varbig=document.createElement("li");big.innerHTML="我是setTimeout插入的内容";divs.appendChild(big);},0);newPromise((resolve,reject)=>{resolve("我是Promise.then插入的内容");}).then((msg)=>{varsmall=document.createElement("li");small.innerHTML=msg;divs.appendChild(small);});
我们获取到ul标签以后,分别用setTimeout和Promise.then给ul标签插入li,setTimeout在前,那么真实渲染到页面上的时候应该是什么样子的呢?
这个时候问题来了?js不是单线程的吗,为什么后面Promise会先进行DOM渲染?
js的执行机制问题(EventLoop机制)
我们都知道js是单线程的一门语言,js代码会一行一行的执行,但是如果碰到定时器,网络请求等这些基于回调函数的任务,我们如果等待他执行完成以后再进行下一步操作的话。这是不合理的,所以我们有一个专门的队列来存放这些任务(如:DOM事件,定时器,网络请求),而这些任务又被称为异步任务。
我们来举一个例子,比如你和你的小伙伴一共五个人同一天入职了字节,办理入职手续的时候,人事小姐姐给你们一人一张入职表,那么这个动作就是单线程执行的动作,因为人事小姐姐会按照顺序挨个发一张表,然后让你们填写信息,那么这个时候如果要等待第一个人填写完成的再给第二个人的话,这样是不合理的,谁先写完谁就交上去,这样才是合理的,那么对应到js中,给完你表格让你去一旁填写,这就是异步任务,那么如果你写完了你不上交怎么办呢?人事小姐姐过一会就会问道,谁写完谁就把表格交上来,那么这个行为对应的就是EventLoop机制,这个机制会不停的询问异步队列里面有没有完成任务的,有的话他就会把完成任务的代码推到主线程里面执行对应的回调函数,没有的话他就会过一会再继续问有没有到了时机的任务,就像一个永动机一样
如上图所示,js代码一行一行的在主线程里面执行,碰到异步就会'记录'下来,放到异步队列里面,等待时机成熟(如定时器时间到了,网络请求回来了),就会移步到Callback Queue里面。 等同步任务都执行完成了以后,Event Loop开始工作,进行轮询查找Callback Queue,如果有任务的话,就把它移动到Call Stack主线程里面进行执行然后继续轮询进行查找,就像永动机一样。
宏任务和微任务
上面我们了解了异步任务执行的时机,那么在异步任务里面,又分为宏任务和微任务
宏任务
微任务
为什么微任务先执行
因为当我们的主线程的代码执行完毕之后,在Event Loop执行之前,首先会尝试DOM渲染,这个时候,微任务是在DOM渲染之前执行,DOM渲染完成了之后,会执行宏任务,这也就是文章开头那段代码的解释,微任务要比宏任务更早执行!!! 那么我们的最终图示如下
总结
js代码的执行顺序和规则(Event Loop)
同步代码,一行一行放在Call Stack执行;
遇到异步,会先‘记录’下来,等待时机(如定时器,网络请求等等);
时机到了,就会移步到Call Queue;
如过call Stack为空,就是同步代码执行完毕之后,会尝试DOM渲染(微任务在DOM渲染前执行,宏任务在DOM渲染后执行);
Event Loop 开始工作,轮询查找Callback Queue,如果有代码就移动到Call Stack中;
不停的轮询查找。
如果有哪些不对的地方还希望您能指出啦共同讨论学习,共同进步,谢谢!