首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >循环中的异步&&循环中的闭包

循环中的异步&&循环中的闭包

作者头像
陌上寒
发布2019-04-02 15:09:44
1.5K0
发布2019-04-02 15:09:44
举报
文章被收录于专栏:前端进阶前端进阶

for循环中let 和var的区别

var 是函数级作用域或者全局作用域,let是块级作用域 看一个例子

    function foo() {
      for (var index = 0; index < array.length; index++) {
        //..循环中的逻辑代码
      }
      console.log(index);//=>5
    }
    foo()
   console.log(index)//Uncaught ReferenceError: index is not defined

foo函数下的index输出5,全局下的index不存在 现在我们把var 换为let

    function foo() {
      for (let index = 0; index < array.length; index++) {
        //..循环中的逻辑代码
      }
      console.log(index)//Uncaught ReferenceError: index is not defined
    }
    foo()

报错了,index不在foo函数作用域下,当然肯定也不会再全局下 因为var和let的这个区别(当然var和let的区别不止于此)所以导致了下面的这个问题 关于var的

    const array = [1, 2, 3, 4, 5]
    function foo() {
      for (var index = 0; index < array.length; index++) {
        setTimeout(() => {
          console.log(index);
        }, 1000);
      }
    }
    foo()

看下输出

关于let的

    const array = [1, 2, 3, 4, 5]
    function foo() {
      for (let index = 0; index < array.length; index++) {
        setTimeout(() => {
          console.log(index);
        }, 1000);
      }
    }
    foo()

看下输出

因为var和let 在作用域上的差别,所以到这了上面的问题 使用var 定义变量的时候,作用域是在foo函数下,在for循环外部,在整个循环中是全局的,每一次的循环实际上是为index赋值,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5; let的作用局的块级作用局,index的作用域在for循环内部,即每次循环的index的作用域就是本次循环,下一次循环重新定义变量index;所以index每次循环的输出都不同 这里还有另外一个问题,setTimeout,这是一个异步,这就是我们今天要讨论的

循环中的异步

setTimeout(func,time)函数运行机制

setTimeout(func,time)是在time(毫秒单位)时间后执行func函数。浏览器引擎按顺序执行程序,遇到setTimeout会将func函数放到执行队列中,等到主程序执行完毕之后,才开始从执行队列(队列中可能有多个待执行的func函数)中按照time延时时间的先后顺序取出来func并执行。即使time=0,也会等主程序运行完之后,才会执行。

一个需求,一个数组array[1,2,3,4,5],循环打印,间隔1秒

上面的let是循环打印了12345,但是不是间隔1s打印的,是在foo函数执行1s后,同时打印的

方式一 放弃for循环,使用setInterval
    function foo(){
      let index = 0;
      const array = [1, 2, 3, 4, 5]

      const t = setInterval(()=>{
        if (index < array.length) {
          console.log(array[index]);
        }
        index++;
      }, 1000);

      if (index >= array.length) {
        clearInterval(t);
      }
    }
    foo()

我们上面说到,当for循环遇到了var,变量index的作用域在foo函数下,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5;

方式二,引入全局变量

代码执行顺序是,先同步执行for循环,再执行异步队列,在for循环执行完毕后,异步队列开始执行之前,index经过for循环的处理,变成了5。 所以我们引入一个全局变量j,使j在for循环执行完毕后,异步队列开始执行之前,依然是0,在异步执行时进行累加

    var j = 0;
    for (var index = 0; index < array.length; index++) {
      setTimeout(() => {
        console.log(j);
        j++;
      }, 1000 * index)
    }
方式三 for循环配合setTimeout(常规思路,不赘述,面试必备技能)
    const array = [1, 2, 3, 4, 5]
    function foo() {
      for (let index = 0; index < array.length; index++) {
        setTimeout(() => {
          console.log(index);
        }, 1000*index);
      }
    }
    foo()
方式四,通过闭包实现

开始讨论方式四之前我推荐先阅读一遍我之前写过一篇文章 谈一谈javascript作用域 我们对上面的问题再次分析,for循环同步执行,在for循环内部遇到了setTimeout,setTimeout是异步执行的,所以加入了异步队列,当同步的for循环执行完毕后,再去执行异步队列,setTimeout中有唯一的一个参数数index 方式三可行,是因为let是块级作用域,每次for执行都会创建新的变量index,for循环执行完毕后,异步执行之前,创建了5个独立的作用域,5个index变量,分别是0,1,2,3,4,相互独立,互不影响,输出了预期的结果 如果说每次循环都会生成一个独立的作用域用来保存index,问题就会得到解决,所以,我们通过闭包来实现

    const array = [1, 2, 3, 4, 5]

    function foo() {
      for (var index = 0; index < array.length; index++) {
        function fun(j) {
          setTimeout(function () {
            console.log(j);
          }, 1000 * j);
        }
        fun(index)
      }
    }
    foo()

setTimeout中的匿名回调函数中引用了函数fun中的局部变量j,所以当fun执行完毕后,变量j不会被释放,这就形成了闭包 当然我们可以对此进行一下优化

    const array = [1, 2, 3, 4, 5]

    function foo() {
      for (var index = 0; index < array.length; index++) {
        (function(j) {
          setTimeout(function () {
            console.log(j);
          }, 1000 * j);
        })(index)
      }
    }
    foo()

将foo函数改为匿名的立即执行函数,结果是相同的

总结

for循环本身是同步执行的,当在for循环中遇到了异步逻辑,异步就会进入异步队列,当for循环执行结束后,才会执行异步队列 当异步函数依赖于for循环中的索引时(一定是存在依赖关系的,不然不会再循环中调动异步函数)要考虑作用域的问题, 在ES6中使用let是最佳的选择, 当使用var时,可以考虑再引入一个索引来替代for循环中的索引,新的索引逻辑要在异步中处理 也可以使用闭包,模拟实现let 在实际开发过程中,循环调用异步函数,比demo要复杂,可能还会出现if和else判断等逻辑,具体的我们下次再续

参考 通过for循环每隔两秒按顺序打印出arr中的数字 setTimeOut和闭包 《你不知道的JavaScript》上卷

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • for循环中let 和var的区别
  • 循环中的异步
    • setTimeout(func,time)函数运行机制
      • 一个需求,一个数组array[1,2,3,4,5],循环打印,间隔1秒
        • 方式一 放弃for循环,使用setInterval
        • 方式二,引入全局变量
        • 方式三 for循环配合setTimeout(常规思路,不赘述,面试必备技能)
        • 方式四,通过闭包实现
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档