美文网首页
setTimeout函数相关问题

setTimeout函数相关问题

作者: Mavericker | 来源:发表于2018-06-10 08:57 被阅读0次

setTimeout函数是面试中经常拿来出题的一个函数,可以引出很多比较关键的知识点,这篇文章就通过一个setTimeout函数考了前端好多知识。这里特地来总结一下

  1. 先看看这种很常见的问题吧:
    for (var i = 1; i <= 5; i++) {
        setTimeout( function timer(){
            console.log(i);
        },i*1000);
    }
    

    先说下上面代码的运行结果:运行时会以每秒一次的频率输出五次6.-

  2. 为什么每秒一次?

    先解释下一些相关概念吧,这里参考了这篇博客

    • 异步模式:即与同步模式相反,可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调。 最常见的异步模式就数定时器了,我们来看看以下的例子。

      setTimeout(function() {
          console.log('taskA, asynchronous');
      }, 0);
      console.log('taskB, synchronize');
      //while(true);
      
      -------ouput-------
      taskB, synchronize
      taskA, asynchronous
      

      我们可以看到,定时器延时的时间明明为0,但taskA还是晚于taskB执行。这是为什么呢?由于定时器是异步的,异步任务会在当前脚本的所有同步任务执行完才会执行。如果同步代码中含有死循环,即将上例的注释去掉,那么这个异步任务就不会执行,因为同步任务阻塞了进程。

    • 回调函数: 提起异步,就不得不谈谈回调函数了。上例中,setTimeout里的function便是回调函数。可以简单理解为:(执行完)回(来)调(用)的函数。以下是WikiPedia对于callback的定义。

      In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time.

      可以看出,回调函数是一段可执行的代码段,它以「参数」的形式传递给其他代码,在其合适的时间执行这段(回调函数)的代码。

      WikiPedia同时提到

      The invocation may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback.

      也就是说,回调函数不仅可以用于异步调用,一般同步的场景也可以用回调。在同步调用下,回调函数一般是最后执行的。而异步调用下,可能一段时间后执行或不执行(未达到执行的条件)。

    • setTimeout函数:setTimeout的意思是传递一个函数,延迟一段时候把该函数添加到队列当中,并不是立即执行;所以说如果当前正在运行的代码没有运行完,即使延迟的时间已经过完,该函数会等待到函数队列中前面所有的函数运行完毕之后才会运行;也就是说所有传递给setTimeout的回调方法都会在整个环境下的所有代码运行完毕之后执行

    所以,刚进入for循环的时候,i为1,所以相对于现在延迟一秒将timer函数添加到队列当中,然后for循环还要继续啊,并没有等一秒再继续循环啊,然后进行第二次循环,这时候i为2,所以相对于现在延迟两秒将timer函数送进队列。以此类推。for循环的时间忽略不计的话,timer函数就以每秒一次的频率执行啦。
  3. 为什么每次都显示6?

    感觉与闭包和异步都有关系。

    由于异步,导致for循环结束之后再执行setTimeout中的回调函数,而此时i已经变成6,由于闭包,导致回调函数中的i与for循环中的i一直保持引用关系,所以每次都输出6。

  4. 更改方法

    这篇文章在最后给了5种解决方案,下面给出两个比较简单的

    1. 用立即执行函数,使得匿名函数内的变量i与外部函数的变量i的引用关系断掉

      for(var i = 0;i<=5;i ++) {
        (function(i){
          setTimeout(function timer() {
            console.log(i)
          }, i * 1000);
        })(i);
      }
      
    2. 利用ES6引入的let关键字

      for(let i = 0;i<5;i++) {
        setTimeout(function timer(){
          console.log(i);
        }, i * 1000);
      }
      

相关文章

网友评论

      本文标题:setTimeout函数相关问题

      本文链接:https://www.haomeiwen.com/subject/wltfeftx.html