博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
js单线程执行引起的setTimeout和ajax执行的迷之bug
阅读量:2429 次
发布时间:2019-05-10

本文共 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/

你可能感兴趣的文章
exceptional c++:47个c++工程难题、编程问题和解决方案(中文版)
查看>>
黎曼猜想漫谈
查看>>
CRM基础教程
查看>>
内存数据管理(第2版)
查看>>
深入理解Android:卷II
查看>>
QTP自动化测试最佳实践
查看>>
捉虫日记
查看>>
jQuery Mobile权威指南
查看>>
决战第三屏:移动互联网时代的商业与营销新规则
查看>>
如何清理ibdata1
查看>>
Think HY 读《观止-微软》一书有感
查看>>
方刚先生谈《胜于言传——网站内容制胜宝典》
查看>>
【Frank.Xu】.net深入学习笔记(3):垃圾回收与内存管理
查看>>
2006JAVA类图书读者投票排行榜
查看>>
《编程之美》舞动’08年IT图书销售奇迹
查看>>
MySQL查询的性能优化
查看>>
微博的MySQL数据库优化实践经验
查看>>
php以图搜图
查看>>
php怎么实现根据图片搜索图片功能
查看>>
三种保证URL地址可信的加密方式
查看>>