本文共 2732 字,大约阅读时间需要 9 分钟。
问题描述:
近期,在做倒计时动画时,用到了setTimeout()这个计时函数,在使用时,由于不太理解js的执行原理,光看函数的用法写出了一段代码,结果发现setTimeout的执行非常的迷,和自己想象的完全不同,行为可以用“诡异”来形容。同样的问题在我写一个ajax异步请求时,又遇到了一个神奇的执行次序,由这两个bug情景,我认识到了理解js执行原理的重要性。
情景一:
for(var i = 0;i<100;i++){ setTimeout(function(){ console.log('exeute'); },1000);}
以上这段代码按照我之前的理解,应该是每隔1S在控制台输出一个'exeute',但是可以按F12在控制台运行一下试试,结果是在停顿1s后,立即输出了100个'exeute',这有些难以理解。
情景二:
export default { data(){ return { message:[] // 这是一个数组 } } method:{ getMessage(){ /* * 如果信息仓库里存储着信息,就直接赋值 * 如果为空,就异步请求 */ if (store.length===0) { axios({ method: 'get', url: '/common/' }) .then(function (response) { if (response) { this.message = store = response; } }) .catch(function (error) { console.log(error); }); } else{ // 直接用仓库里的数据赋值 message = store; } // 赋完值就跳转到 /newpath 新路由 this.$router.push({path:'/newpath'}); } }}
按照预想,应该是message获得值后,就跳转到新的界面,然而事实上,当跳转到新页面后,需要的message却是空的,这种行为也是很迷。
问题分析:
关于Js的单线程执行,必备的背景知识
浏览器的每一个窗口都只有一个执行js脚本的线程,在每一个特定的时间,只有一个特定的代码被执行,会阻塞其他的代码,整个执行就像一条流水线,未执行的代码加入执行队列的先后排序,浏览器会按次序执行这些代码。
但是我们在写js时,有着大量的异步事件和事件监听,如果只有一个线程执行,怎么能实现呢?
要注意,浏览器不是单线程的,在一个窗口下,通常有
一个javascript执行线程,界面渲染线程,浏览器事件触发线程,Http请求线程
其中,浏览器事件触发线程是一个内部大消息循环,会轮询事件监听队列中收到监听的信号或事件,与js执行线程是并行的,如果一个监听的事件触发比如一次点击,轮询线程就会将点击对应的处理代码加入js执行线程队列中,让它等待去执行。
理解了事件驱动的原理,再来理解setTimeout的执行:
当遇到调用setTImeout()时,js引擎会启动定时器,在大约的执行时间到了,定时器就会将setTimeout对应的代码加入js执行线程去执行,起到延时执行的效果。
最后来看看异步请求是如何实现的:
当遇到异步请求代码时,,浏览器会将请求交给上文提到的Http请求线程去发送请求,之后有一段时间的网络传输时间,请求线程就处于轮询监听状态,一旦有消息接收到,就将异步请求接收信息的处理代码加入到js执行队列中等待执行。
网络请求的延时不会对当前页面和动作的执行产生任何影响,这样就达到了异步请求的效果。
..........................................................................................................................................................................................................................................
在理解了js的相关原理后,再来看看问题描述中的情景一:
在100次循环执行
setTimeout(function(){
console.log('exeute');},1000);
时,浏览器会几乎瞬间的将100次对setTimeout的调用交给计时器去处理,所以这100次的打印输出都是在大约1s后加入js执行队列,得到执行,所以就出现了100个'exeute'几乎同时的打印出来 的现象。
如果想要每隔一秒打印一个'exeute',需要这样写:
for(var i = 0;i<100;i++){ setTimeout(function(){ console.log('exeute'); },1000*i);}
即将设定的定时时间 依次设定成 1s,2s,3s .........即可,到了需要的时间,代码就会加入js执行队列得到执行。
..........................................................................................................................................................................................................................................
情景二的分析:
执行器在遇到ajax函数时,会将请求交给Http请求线程去执行发送,等待和接收的动作,自己不会等待,只会立马执行之后的路由跳转代码,立即实现页面的跳转,当跳转完成时,请求线程还没有接收到信息,所以新页面的message数据就是空的。
要想实现在message得到数据后跳转,需要将页面跳转代码放到 ajax接收到信息后的回调函数中,这样在接收到信息就才会执行页面的跳转,到达预想的目标。
在理解js的执行原理后,如果以后遇到类似的异步请求,异步时间监听,如果遇到一些奇异的bug,首先就要回想一遍js的执行流程,这样就能正确的分析代码的执行过程,快速的debug了。
转载地址:http://xijmb.baihongyu.com/