function createFunction1(){
for(var i=0;i<5;i++){
function s(){
console.log(i);
}
s();
}
}
createFunction1(); //0 1 2 3 4;
这是一个正常的函数
function createFunction2(){
for(var i=0;i<5;i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
}
}
createFunction2(); //每隔1秒输出‘5’、共输出5次
并不会按照我们预想的每隔1秒分别输出0、1、2、3、4
原因:此函数在for循环的第一层是setTimeout函数,他的执行和createFunction1中的s函数一样,将按分别在1秒后、2秒后、3秒后执行。但这儿需要注意的是,setTimeout的内部函数timer并没有立即执行,for循环中的i将会把值分别赋给setTimeout外部参数中的i,但其内部函数timer()则只会引用包含函数setTimeout()中的变量的最后一个值。因为闭包所保存的是整个变量对象,而不是某个特殊的变量。当然其中的这些处理变化,都是瞬间完成的,与执行时间并无关系,即使把1000改成0效果还是一样的。
重写一下这个函数:
function createFunction3(){
for (var i=0;i<5;i++){
(function(i){
setTimeout(function timer(){
console.log(i);
},i*1000);
})(i);
}
}
createFunction3(); //每隔1秒分别输出0 1 2 3 4
这个例子,给外部包装了一个立即执行的匿名函数,setTimeout里面的匿名函数不再引用外部函数的参数,而是直接引用外部匿名函数的参数,这时,一切就会按照我们预想的来执行了
另外ES6为我们提供了另一种解决方案:
for (let i=1; i<5; i++) {
setTimeout( function timer(){
console.log( i );
}, 1000 );
} //每隔1秒分别输出0 1 2 3 4
这两种方法都不是解决异步问题的,而是解决变量作用域的问题的。因为函数 timer() 属于一个新的域,通过 var 定义的变量是无法传入到这个函数执行域中的,于是第一种是通过传入参数,间接的把变量传入到 timer 中;第二种是通过使用 let 来声明块变量,这时候变量就能作用于这个块,所以 timer 就能使用 i 这个变量了
setTimeout经常被用于延迟执行某个函数
setTimeout(function(){}, timeout);
有时为了进行异步处理,而使用setTimeout(function…,0);
function f(){
// get ready
setTimeout(function(){
// do something
}, 0);
return …;
}
在setTimeout设定的函数处理器之前,函数f返回;
在使用异步处理时,尤其是使用闭包特性时,要特别小心
for(var i = 0 ; i < 10; i++){
setTimeout(function(){
console.log(i);
}, 0);
}
对于初次使用这种方式的同学来说,很可能会认为程序会打印0…9,可结果确实打印10个10;问题就在于,当循环完成时,function得到执行,而i已经变成10,console.log(i)中使用的是10!
那么可以换一种方式,用函数参数来保存0….9(其实也是利用了闭包):
for(var i = 0 ; i < 10; i++){
setTimeout((function(i){
return function(){
console.log(i);
}
})(i), 0);
}
另外再来看一个例子:
function test() {
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i)
}, 1000);
}
}
test(); //9 9 9 9 9 9 9 9 9 9
把他修改一下:
function test() {
for (var i = 0; i < 10; i++) {
setTimeout(console.log(i), 1000);
}
}
test(); //1 2 3 4 5 6 7 8 9
来看一道考察JavaScript运行机制的题目
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5); //2 3 5 4 1
这道题考察JavaScript的运行机制。首先先碰到一个 setTimeout,于是会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,因此开始肯定不会输出 1 。然后是一个 Promise,里面的函数是直接执行的,因此应该直接输出 2 3 。然后,Promise 的 then 应当会放到当前 tick 的最后,但是还是在当前 tick 中。因此,应当先输出 5,然后再输出 4 。最后在到下一个 tick,就是 1