传智播客旗下技术交流社区北京校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© chennaiweng 中级黑马   /  2019-1-10 12:22  /  104 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 chennaiweng 于 2019-1-10 12:28 编辑

for循环中有setTimeout,是我们在闭包案例中经常遇到和解决的一个问题,看一下下面这个案例

for (var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);
    console.log(i);
}
这个题目的打印结果是什么呢?

分析
对于这个题目,我们可以这么考虑,先把for循环里面的内容抽出来

   setTimeout(function() {
        console.log(i);
    }, 0);
    console.log(i);
对于这个,肯定是先执行 console.log(i),后执行setTimeout。那么,我们把上面的for循环拆解开来,如下:

var i = 0;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
因为setTimeout是注册事件,要等到当前脚本的同步任务和“任务队列”中已有的事件,全部处理完以后,才能执行。因此,上面执行结果是把所有的console都执行完毕之后才能执行setTimeout,因此,执行结果如下:

0
1
2
3
3
3

for循环和setTimeout
这个我之前文章中好像有提及过,针对这种情况,我们将上面案例如下修改,就可以将setTimeout也可以输出1,2,3.

我们将setTimeout里面的匿名函数修改成自调用匿名函数

function() {
        console.log(i);
    }
修改成

(function(n) {
        console.log(n);
    })(i)
因为setTimeout的第一个参数必须是fn,因此,我们要对这个匿名函数返回一个function

(function(n) {
            return function() {
                console.log(n);
            };
        })(i)
那么,代码如下:

for (var i = 0; i < 3; i++) {
    setTimeout((function(n) {
        return function() {
            console.log(n);
        };
    })(i), 0);
    console.log(i);
}
这样运行结果就是

0
1
2
0
1
2

应用
我们看下面的运行结果

console.log(1);

setTimeout(function() {
  console.log(2);
}, 0);

Promise.resolve().then(function() {
  console.log(3);
}).then(function() {
  console.log(4);
});

console.log(5);

// 1
// 5
// 3
// 4
// 2
上面代码的执行结果说明,setTimeout(fn, 0)在Promise.resolve之后执行。这是因为setTimeout语句指定的是“正常任务”,即不会在当前的Event Loop(事件循环)执行。而Promise会将它的回调函数,在状态改变后的那一轮Event Loop(事件循环)指定为微任务。所以,3和4输出在5之后、2之前。

setTimeout(fn, 0)的一大应用是,可以调整事件的发生顺序。比如,网页开发中,某个事件先发生在子元素,然后冒泡到父元素,即子元素的事件回调函数,会早于父元素的事件回调函数触发。如果,我们先让父元素的事件回调函数先发生,就要用到setTimeout(fn, 0)。

案例如下:

document.getElementById("haoroomsID").onclick = function A() {
  setTimeout(function B() {
    console.log("触发子元素事件")
  }, 0)
};

document.body.onclick = function C() {
console.log("触发父元素事件")
};
上面案例,点击haoroomsID会先触发父级元素事件,然后再触发子元素事件。
分享至 : QQ空间
收藏

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马