为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Html/CSS

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

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执行完毕,执行最后一个输出iPromise.all(tasks).then( () => {
    setTimeout( () => {        console.log(new Date, i)
    }, 1000)
})

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

// 模拟sleepconst 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);
})();



作者:宿雨jj
链接:https://www.jianshu.com/p/25d26fbc99ac


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消