美文网首页让前端飞饥人谷技术博客
从闭包引出来的一系列问题

从闭包引出来的一系列问题

作者: cce117b0a0ce | 来源:发表于2018-08-28 08:25 被阅读3次

    从闭包引出来的一系列问题

    1. 不起眼的开始

    for(var i = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(new Date, i)
        }, 1000)
    }
    console.log(new Date, i)
    

    很明显,由于异步的作用。到最后输出的结果为6个5

    如果用箭头表示前后两次输出有1s的间隔,用,代表前后一起输出,那么输出结果是5->5,5,5,5,5

    这个也很容易的就可以进行解释,先执行console.log(),再进行setTimeout()的异步操作。

    追问1:如果变成 5 -> 0,1,2,3,4 该怎样处理?

    首先可以使用闭包来解决这个问题:

    for(var i = 0; i < 5; i++) {
        (function(j) {
            setTimeout(function() {
                console.log(new Date, j)
            }, 1000)
        })(i)
    }
    console.log(new Date, i) // 5
    

    利用立即执行函数,来解决闭包造成的问题。

    此外还可以使用setTimeout的第三个参数 文档

    for(var i = 0; i < 5; i++) {
        setTimeout(function(j) {
            console.log(new Date, j)
        }, 1000, i)
    }
    console.log(new Date, i) // 5
    

    可能会有很多同学采用ES6的方式来避免:

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

    但是此时并不能正确输出我们想要的结果,因为let声明的 i 产生了块级作用域,导致 for 循环外面的输出不能获取的 i ,所以此时会报错 i is not defined

    追问2:如果把输出变为 0->1->2->3->4->5 呢?

    其中一种比较容易想到的方法:

    for(var i = 0; i < 5; i++) {
        (function(j) {
            setTimeout(function() {
                console.log(new Date, j)
            }, 1000*j)
        })(i)
    }
    
    setTimeout(function() {
        console.log(new Date, i)
    }, 1000*i)
    

    但是js中定时器的触发时机是不确定的,每次循环都会产生一个异步操作之后,会有一个输出,那么完全可以使用Promise来解决这个问题。

    const tasks = []
    for(var i = 0; i < 5; i++) {
        (function(j){
            tasks.push(new Promise((resolve) => {
                setTimeout(() => {
                    console.log(new Date, j)
                    resolve()
                }, j*1000)
            }))
        })(i)
    }
    
    Promise.all(tasks).then( () => {
        setTimeout( () => {
            console.log(new Date, i)
        }, 1000)
    })
    

    将上面代码处理一下:

    const tasks = []
    const output = function(i) {
        new Promise( (resolve) => {
            setTimeout( () => {
                console.log(new Date, i)
                resolve()
            }, 1000*i)
        })
    }
    
    for(var i = 0;i < 5; i++) {
        tasks.push(output(i))
    }
    
    // 全部Promise执行完毕,执行最后一个输出i
    Promise.all(tasks).then( () => {
        setTimeout( () => {
            console.log(new Date, i)
        }, 1000)
    })
    

    追问3:使用 async / await 怎么实现

    // 模拟sleep
    const sleep = (time) => new Promise((resolve) => {
        setTimeout(resolve, time);
    });
    
    (async () => {  // 声明即执行的 async 函数表达式
        for (var i = 0; i < 5; i++) {
            if (i > 0) {
                await sleep(1000);
            }
            console.log(new Date, i);
        }
    
        await sleep(1000);
        console.log(new Date, i);
    })();
    

    相关文章

      网友评论

        本文标题:从闭包引出来的一系列问题

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