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

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

1. 不起眼的开始

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

很明显,因为异步的做用。到最后输出的结果为6个5javascript

若是用箭头表示先后两次输出有1s的间隔,用,表明先后一块儿输出,那么输出结果是5->5,5,5,5,5java

这个也很容易的就能够进行解释,先执行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

利用当即执行函数,来解决闭包形成的问题。async

此外还可使用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的方式来避免:code

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 definedip

追问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);
})();
相关文章
相关标签/搜索