一个定时器相关的题目引发的思考

  定时器是我们经常使用的一个异步函数,它的用处十分广泛,比如图片轮播、各种小的动画、延时操作等等;

  定时器函数只有两个setTimeout、setInterval,这两个工作原理相同,唯一的区别是:setTimeout只执行一次,setInterval循环执行;

  通过以下实例看看对定时器原理掌握程度:

  定时器3个实例

    首先声明这三个实例输出皆不同,先思考输出结果,以及为何不同

    实例一:

console.log('test1')
for(var i=0;i<10;i++){
    setTimeout(()=>{console.log(i)},1000);
}
console.log('test2')

    实例二(使用了ES6 let关键字):

console.log('test1')
for(let i=0;i<10;i++){
    setTimeout(()=>{console.log(i)},1000);
}
console.log('test2')

    实例三(使用了ES6 let关键字):

console.log('test1')
for(let i=0;i<10;i++){
    setTimeout(()=>{console.log(i)},1000*i);
}
console.log('test2')

    结果如下:

      实例一:'test1' --> 'test2' -->  同时输出十个10

      实例二:'test1' --> 'test2' -->  同时输出0-9数字

      实例三:'test1' --> 'test2' --> 每哥1s输出一个数字,数字从0-9

    至于原因等会再讲,首先要先明白定时器的工作原理,而定时器的原理正是javascript事件循环模型的体现;其次要掌握闭包;

 

js运行机制:Event Loop

  必须明确一点:javascript是单线程,同一时间只能做一件事;

  单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

 

  所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它主线程。但是实际上还存在其他的线程。例如:处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程(例如在Node.js中)等等。这些线程可能存在于JS引擎之内,也可能存在于JS引擎之外,在此我们不做区分。不妨叫它们工作线程

 

  如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

  JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

  具体来说,异步执行的运行机制如下(以ajax为例):

             

解答上述实例

  第一个实例:

    1.主线程自上向下同步执行,首先输出'test1';

    2.遇到for循环后,发现里面是异步函数定时器,就把定时器放到消息队列中,执行了10次,因此消息队列中有10个定时器消息;注意:此时全局变量i=10;

    3.将定时器放到消息队列中后,主线程继续执行同步任务;输出'test2';

       此时主线程中的同步任务执行完了,主线程上是空的;开始执行消息队列中的消息;

    4.由于定时器延时1s,因此1s后消息队列中的消息进入主线程(注意:此时符合条件的有10个定时器消息);

               在主线程中执行定时器回调函数时,该回调函数是个闭包,i是全局变量,此时的i=10,因此执行结果是10个10;

 

  第二个实例:

     此实例跟第一个实例唯一的区别是 let i 此时i是局部变量;以上四个步骤都是一样的,区别在主线程执行定时器回调函数;此处代码经过babel编译后:

var _loop = function (arg) {
    setTimeout(function () {
        console.log(arg);
    }, 1000);
};

for (var _i = 0; _i < 10; _i++) {
    _loop(_i);
}

      新定义了一个函数_loop,这个是定时器回调函数的父作用域;

    当主线程处理定时器回调函数时,回调函数通过 作用域链 从_loop中获取arg的值;

  延伸实例

    上述方法使用了ES6中的let关键字,如何使用ES5闭包解决这个问题?

    其实,想法就是创建个局部作用域,让定时器的回调函数从局部作用域中读取数值;

for(var i=0;i<10;i++){
    (function(arg){
        setTimeout(function(){
            cosnole.log(arg)
        },1000)
    })(i)
}
// 在for语句中创建立即执行函数,形成一个局部作用域;
// 定时器的回调函数从局部作用域中读取数值

  还有别的方法吗?

  可以从定时器的回调函数入手,将这个参数构造成闭包;

function backFun(arg){
    return function(){
        console.log(arg)
    }
}
for(var i=0;i<10;i++){
    setTimeout(backFun(i),1000)
}
// 同样是利用闭包创建局部作用域
// 定时器的回调函数 读取的仍是局部作用域中的数值

 

总结一下,这个题目考察了哪些知识点:

  1.闭包

  2.作用域链

  3.定时器运行的原理(异步执行原理)

  4.js运行机制

 

参考:(感谢以下文档)

[1] javascript: 彻底理解同步、异步、事件循环(Event Loop)
[2] javascript 运行机制详解:在谈Event Loop 

[3] 如何使用定时器传递参数

[4] 什么是Event Loop

转载于:https://www.cnblogs.com/RocketV2/p/7240971.html

# 高校智慧校园解决方案摘要 智慧校园解决方案是针对高校信息化建设的核心工程,旨在通过物联网技术实现数字化校园的智能化升级。该方案通过融合计算机技术、网络通信技术、数据库技术和IC卡识别技术,初步实现了校园一卡通系统,进而通过人脸识别技术实现了更精准的校园安全管理、生活管理、教务管理和资源管理。 方案包括多个管理系统:智慧校园管理平台、一卡通卡务管理系统、一卡通人脸库管理平台、智能人脸识别消费管理系统、疫情防控管理系统、人脸识别无感识别管理系统、会议签到管理系统、人脸识别通道管理系统和图书馆对接管理系统。这些系统共同构成了智慧校园的信息化基础,通过统一数据库和操作平台,实现了数据共享和信息一致性。 智能人脸识别消费管理系统通过人脸识别终端,在无需接触的情况下快速完成消费支付过程,提升了校园服务效率。疫情防控管理系统利用热成像测温技术、视频智能分析等手段,实现了对校园人员体温监测和疫情信息实时上报,提高了校园公共卫生事件的预防和控制能力。 会议签到管理系统和人脸识别通道管理系统均基于人脸识别技术,实现了会议的快速签到和图书馆等场所的高效通行管理。与图书馆对接管理系统实现了一卡通系统与图书馆管理系统的无缝集成,提升了图书借阅的便捷性。 总体而言,该智慧校园解决方案通过集成的信息化管理系统,提升了校园管理的智能化水平,优化了校园生活体验,增强了校园安全,并提高了教学和科研的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值