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

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

作者: 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);
})();

相关文章

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

    从闭包引出来的一系列问题 1. 不起眼的开始 很明显,由于异步的作用。到最后输出的结果为6个5 如果用箭头表示前后...

  • JavaScript闭包

    什么是闭包? 闭包(closure)我们从汉字的字面意思可以理解为:闭:封闭的 闭合 关闭包:包裹 ...

  • Swift中的闭包

    在Swift中有两种闭包,逃逸闭包(@escaping)和非逃逸闭包(@nonescaping)。从Swift 3...

  • 「Python」闭包

    闭包的条件 闭包,从字面意思上可能不太好理解是什么意思,但是从闭包的条件入手会相对比较好理解。闭包需要满足三个条件...

  • swift-闭包

    闭包 闭包定义 闭包简化 - 尾随闭包 闭包参数 闭包返回值 闭包的循环引用

  • Python简明教程第14节:闭包和装饰器

    闭包 首先还是得从基本概念说起,什么是闭包? 维基百科:在计算机科学中,闭包(Closure)是词法闭包(Lexi...

  • js面试知识点汇总

    1.什么是闭包?举例说明从作用域链谈闭包闭包就是能够读取其他函数内部变量的函数,闭包实现累加效果 function...

  • 《Swift从入门到精通》(六):闭包

    闭包(Closures)闭包有三种形式:全局函数是具有名称且不捕获任何值的闭包。嵌套函数是具有名称的闭包,可以从其...

  • 闭包-Closures [swift 5.1]

    闭包的语法 尾随闭包 闭包逃离 自动闭包

  • Python闭包

    闭包 = 环境变量 + 函数 调用闭包内部的环境变量 闭包的经典误区 闭包与非闭包实现人类走路 非闭包 闭包

网友评论

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

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