setTimeout用法(Event Loop简介、for循环中应用、vue中应用)

setTimeout基本用法

setTimeout(code,millisec)

setTimeout函数接受两个参数,第一个参数code是将要推迟执行的函数名或者一段代码,第二个参数millisec是推迟执行的毫秒数。

例如:

setTimeout('console.log(2)',100);//直接在setTimeout中直接执行代码, 需要以字符串的形式去写,引擎内部会将字符串转为可执行的代码
setTimeout(function(){console.log(2)},100);

什么是 Event Loop

Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制。

JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题。

在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")

 

上图主线程的绿色部分,还是表示运行时间,而橙色部分表示空闲时间。每当遇到I/O的时候,主线程就让Event Loop线程去通知相应的I/O程序,然后接着往后运行,所以不存在红色的等待时间。等到I/O程序完成操作,Event Loop线程再把结果返回主线程。主线程就调用事先设定的回调函数,完成整个任务。

可以看到,由于多出了橙色的空闲时间,所以主线程得以运行更多的任务,这就提高了效率。这种运行方式称为"异步模式"(asynchronous I/O)或"非堵塞模式"(non-blocking mode)。

执行顺序

console.log(1);
//Time1
setTimeout(function(){
    console.log(2);
},300);for (var i = 0;i<10000;i++) {
    console.log(4);
}
//Time2
setTimeout(function(){
    console.log(3);
},300);

运行结果:循环中的方法会先执行,然后再执行settimeout中的方法。

 

 for循环中的setTimeout(异步操作)

function test() {
    for (var i = 0; i < 5; ++i) {
        setTimeout(function() {
            console.log("index is :", i);
        }, 1000);
    }
}
test();

结果会显示:

该操作几乎是在同一时间完成,setTimeout定时根本就没有起作用,这是因为:单线程的js在操作时,对于这种异步操作,会先进行一次“保存”,for循环执行结束后,i的值已经变成5,setTimetout调用均是在for循环结束后进行的,所以自然而然输出都是5。一般情况下,我们使用递归实现:

function test(i) {
    if (i< 5) {
        console.log("index is :", i);
        setTimeout(function() {
            box7(i+ 1);
        }, 1000)
    }
}
test(0);

 我们也可以使用ES6中 async     Promise 实现,代码如下:

var asyncFunc = function(arr, i) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            arr.push(i);
            console.log("index is : ", i);
            resolve();//异步操作完成后执行resolve方法,配合.then使用,本例子中未使用到。
        }, 1000);
    });
}
var box5 = async function() {
    var arr = [];
    for (var i = 0; i < 5; i++) {
        await asyncFunc(arr, i);//async函数执行时,如果遇到await就会先暂停执行,等待Promise对象异步操作完成后,恢复async函数的执行并返回解析值。
    }
    console.log(arr);
}
box5(); 

 补充20200611:

解决办法一

for (var i = 0; i < 5; i++) { 
     (function(i){      //立刻执行函数
        setTimeout(function (){
            console.log(i);  
         },1000);  
     })(i);    
}
这里用到立刻执行函数。这样 console.log(i); 中的i就保存在每一次循环生成的立刻执行函数中的作用域里了。

解决办法二

for (let i = 0; i < 5; i++) {     //let 代替 var
    setTimeout(function (){
        console.log(i);  
     },1000);  
}

let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。

setTimeout在vue中应用

vue中直接使用this,此时的this指向的是window对象,虽然end方法仍然被执行了,但是没有延迟1s后执行的效果。

setTimeout(this.end(),1000);

正确的做法是方法中将this存在变量that中,此时执行setTimeout函数时,setTimeout函数内的that就会访问到这个变量,就会得到当前对象。

export default {
 methods: {
  start: function () {
   let that=this
   setTimeout(function()  {
    that.end()
   }, 1000);
  }
 }

也可以使用箭头函数:

export default {
  methods: {
   start: function () {
    setTimeout(() => {
     this.end()
    }, 1000);
   }
  }
 

 参考:

http://www.ruanyifeng.com/blog/2013/10/event_loop.html

posted @ 2020-06-10 23:27  梁涛999  阅读(1984)  评论(0编辑  收藏  举报