美文网首页网页前端后台技巧(CSS+HTML)
web前端入门到实战:JS中的for循环——你可能不知道的点。

web前端入门到实战:JS中的for循环——你可能不知道的点。

作者: 大前端世界 | 来源:发表于2020-02-03 16:11 被阅读0次

    提出问题

    问题1:

    看一段for循环的代码,大家先想一下执行结果是什么?

    var arr = [2,4,6,8,10];
    var arrLength = arr.length;
    
    for (var i = 0; i < arrLength; i++) {
        setTimeout(function() {
            console.log(i);
            console.log(arr[i]);
        }, 2000);
    }
    
    

    问题2:

    for循环中出现多个异步函数(比如ajax请求,或者node后端执行一些数据库操作或文件操作),如果想要这些异步串行变为同步应该怎么做?

    问题1解决与相关讲解

    结果

    预期结果

    0 2  1 4  2 6  3 8  4 10
    
    

    运行后的结果

    5 undefined  5 undefined  5 undefined  5 undefined  5 undefined
    
    

    产生结果的原因

    setTimeout()函数回调属于异步任务,会出现在宏任务队列中,被压到了任务队列的最后,在这段代码应该是for循环这个同步任务执行完成后才会轮到它,所以for循环在遍历过程中i不断加1,直到i判断失败一次才停止,这时候i为5,也就是说空跑了5次循环。等到了setTimeOut预定的时间后就会执行在for遍历过程中声明的5个setTimeout。所以最终运行后会出现上面的结果,与预期结果不符。

    正确执行的解决方案

    1. 闭包,立即执行函数

    想要得到预期的结果,第一种办法是使用闭包,在闭包函数内部形成了局部作用域,每循环一次,形成一个自己的局部作用域,不受外部变量变化的影响。代码如下:

    
    专门建立的学习Q-q-u-n: 784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习技巧
    (从零基础开始到前端项目实战教程,学习工具,全栈开发学习路线以及规划)
    var arr = [2,4,6,8,10];
    var arrLength = arr.length;
    
    for (var i = 0; i < arrLength; i++) {
        (function(i) {
            setTimeout(function() {
                console.log('i是' + i);
                console.log('value是' + arr[i]);
            }, 2000);
        })(i);
    }
    
    

    2. let

    将代码中的var改成let,let非常适合用于 for循环内部的块级作用域。JS中的for循环体比较特殊,每次执行都是一个全新的独立的块作用域,用let声明的变量传入到 for循环体的作用域后,不会发生改变,不受外界的影响。

    代码如下:

    var arr = [2,4,6,8,10];
    var arrLength = arr.length;
    // i虽然在全局作用域声明,但是在for循环体局部作用域中使用的时候,变量会被固定,不受外界干扰。
    for (let i = 0; i < arrLength; i++) {
        (function(i) {
            setTimeout(function() {
                console.log('i是' + i);
                console.log('value是' + arr[i]);
            }, 2000);
        })(i);
    }
    
    

    问题2解决与相关讲解

    for循环中使用异步,在node.js后端开发或者前端ajax请求的时候还是比较常见的。有多种解决方案

    1. 回调 callback 嵌套异步操作、再回调的方式
    2. Promise + then() 层层嵌套
    3. async和await

    选择我个人认为最优秀的解决方式3async和await进行讲解。

    async + await “外异内同”

    例子:

    如果要去将一批数据发送到服务器,只有前一批发送成功(即服务器返回成功的响应),才开始下一批数据的发送,否则终止发送。这就是一个典型的 “for 循环中存在相互依赖的异步操作” 的例子

    例子对应伪代码:

    async function task () {
        for (let val of [1, 2, 3, 4]) {
            // await 是要等待响应的
            let result = await send(val);
            if (!result) {
                break;
            }
        }
    }
    task();
    
    

    伪代码中使用await之后,实现了异步变成同步的转化,只有for循环中当次对应的发送请求完成且获取结果,才会继续往下执行。

    await几点说明:

    • await执行的那一行语句是同步的。
    • async函数执行后,总是返回一个promise对象,可以理解为这个函数是一个异步函数(外异)但是----------------------引用阮一峰老师书中一句话:

    当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

    node.js后端开发-await在for循环中的应用

    看一段后端项目中应用await的代码:

    
    专门建立的学习Q-q-u-n: 784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习技巧
    (从零基础开始到前端项目实战教程,学习工具,全栈开发学习路线以及规划)
    //dayResult是一个查询到的数组
    for (const item of dayResult)
        {
            //TODO 查询用户vip表 用户体验vip距离到期的用户列表
            let userIds=await db.vip.findAll({
                where:{
                    experience_time:{
                        '$lte':moment().subtract(15-item.day,'day').endOf('day') ,//获取四天前都0时0分秒
                        '$gte':moment().subtract(15-item.day,'day').startOf('day') ,//获取四天前都0时0分秒
                            },
                            vip_type:0
                        },
                    attributes:['user_id',Sequelize.literal(`'${item.id}' as notice_id`)],
                    raw:true
                });
            userNoticeRecord=userNoticeRecord.concat(userIds)
            }
    
    

    相关文章

      网友评论

        本文标题:web前端入门到实战:JS中的for循环——你可能不知道的点。

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