zoukankan      html  css  js  c++  java
  • 定时器存在的弊端,以及解决的方法

    javascript是一大特点是单线程,也就是说同一时间只能做一件事,单线程也就意味着,所有任务需要排队,前一个任务执行完,才会执行后一个任务,如果前面一个任务耗时很长,后一个任务就不得不等。
    如果排队是因为CPU忙不过来也就算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如ajax从操作从网络读取数据),不得不等着结果出来再往下进行。
    javascript语言的设计者意识到,这时候主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务,等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
    于是,所有的任务可以分成两种,一种是同步任务,另一种是异步任务,同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才会执行下一个任务,异步指的是不进入"主线程"而是进入"任务队列的"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
    具体来说,异步执行的运行机制如下:
    1.所有同步任务都在主线程上执行,形成一个执行栈,
    2.主线程之外还存在一个"任务队列",只要异步任务有了结果,就会在"任务队列"中放置一个事件
    3.一旦执行栈中所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件,哪些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
    4.主线程不断重复上面的第三步骤


    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环);

    除了放置异步任务的事件,"任务队列"还可以放置定时事件,setTimeout(fn,0)的含义是,指定任务在主线程最早可得的空闲时间执行,也就是说,尽可能早的执行,他在"任务队列"尾部添加一个事件,因此要等到同步任务和"任务队列"现在的事件执行完以后,才会得到执行


    需要注意的是,定时器只是在指定的时间将事件插入到"任务队列",必须等到执行栈中当前的代码(同步任务以及"任务队列"当前任务)执行完以后,主线程才会执行他指定的回调函数,如果当前代码执行耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定在定时器规定的时间执行,
    比如下面的代码:
    <script>
      for(var i=0;i<900000000;i++){
        // ...
      }
      console.log(0);
      setTimeout(function(){
        console.log(1);
      },1000)
    </script>

    上面的代码,定时器在1000毫秒时加入"任务队列",但是此时同步代码for循环还没有执行完,可能需要很长的时间,所以当for执行完以后会立即输出0和1,0和1的输出也没有体现出间隔1000毫秒的时间,而此时setTime回调函数执行的时候时间远远大于1000毫秒

    再比如:

    < script >
    
    startFn2();
    
    function startFn2() {
    	var p2 = new AlarmClockByInterval(callBackByTest, 2000);
    }
    
    function callBackByTest() {
    	var i = 0;
    	for (; i < 9000000000; i++) {
    
    	}
    	return true;
    }
    
    function AlarmClockByInterval(_args1, _args2) {
    	var timeFn, self = this;
    	callBackFn = _args1;
    	ms = _args2;
    	i = 0;
    	this.getInterval = function() {
    		if (ms) {
    			if (!timeFn) {
    				timeFn = setInterval(function() {
    					console.log("定时任务开始执行:" + new Date().getTime());
    					callBackFn();
    					console.log("定时任务结束执行:" + new Date().getTime());
    				}, ms)
    			}
    		} else {
    			closeInterval();
    		}
    	}
    
    	this.closeInterval = function() {
    		if (timeFn) {
    			closeInterval(timeFn);
    		}
    	}
    	self.getInterval();
    }
    
    < /script>
    

      

     

       
       

    上面的代码,定时任务一旦执行完毕,会立即执行下一次任务,并没有理想中的间隔,直接用setInterval并不能获得我们理想中的效果
    对于上面的问题,我们如何解决呢?
    使用setTimeOut,和递归,利用函数自动调用自身,延时执行就可以很好的解决这个问题

    <script>
    		function AlarmClockByTimeOut(_args1,_args2){
    			var _type = 0, timeFn, _flag = true, ms = _args2, callBackFn = _args1,self = this;
    			this.getTimeOut=function(){
    				var _callee = arguments.callee;
    				if(_flag){
    					if(_type==0){
    						timeFn=setTimeout(function(){
    							console.log("定时任务开始执行:" + new Date().getTime());
    							_flag=callBackFn();
    							console.log("定时任务结束执行:" +new Date().getTime());
    							_callee();
    						},ms)
    					}else{
    						if(timeFn){
    							clearTimeout(timeFn);
    						}else{
    						 console.error(500, "定时器已终止,外部终止...");
    						}
    					}
    				}else{
    					if (timeFn) clearTimeout(timeFn);
                		console.error(500, "定时器已终止,回调函数出现错误或内部强制终止...");
    				}
    
    			}
    			this.close = function(_args1) {
            		_type = _args1 || 1;
       			};
        		self.getTimeOut();
    		}
    		function callBackByTest() {
        		var i = 0;
        		for (; i < 900000000; i++) {
     
       		 	}
        		return true;
    		}
    		function startFn2() {
        		var p1 = new AlarmClockByTimeOut(callBackByTest,2000);
    		}
    		startFn2();
    	</script>
    

      

  • 相关阅读:
    springcloud-spring cloud config统一配置中心
    springcloud-hystrix断路器对微服务的容错处理
    springcloud-feign组件实现声明式的调用
    springcloud-Ribbon-负载均衡组件
    springcloud-Eureka-服务注册与发现核心组件
    springcloud入门-什么是springcloud
    Redis缓存设计及常见问题
    Lucene全文检索入门使用
    redis安装、使用
    nodejs环境 + 入门 + 博客搭建
  • 原文地址:https://www.cnblogs.com/lvruifang/p/7200759.html
Copyright © 2011-2022 走看看