一个 setTimeout 问题小探索

2016-09-13 11:16:57 +08:00
 wesleypursuit

问题

之前有提到一个异步问题,如下代码输出什么: for(var i = 0;i<10;i++){ setTimeout(function(){ console.log(i);
},1000); } 这是我近期遇到的一个面题。当时我并不知道。不过后来面试官告诉我, setTimeout 是异步执行的。并且告诉了我答案。后来在群里边讨论,明白了。会输出 10 个 10 。

探索

为什么呢?首先, setTimeout 是异步执行的。所以在 setTimeout 执行之前,全局变量 i 早就已经变成了 10 。

那么如何解决能让他输出 0-9 呢?其实要解决的就是:在使用 i 的时候保证 i 的值还是我们当时希望的那个值。

如果不考虑兼容性,我们可以使用 es6 的 let ,把每个 i 变成一个局部变量。代码如下: for(let i = 0;i<10;i++){ setTimeout(function(){ console.log(i);
},1000); } 如果要考虑兼容性,那么我们用一个匿名函数,把 i 当做参数传进去,这样我们再用的 i 就是局部变量了。不会受到外边的影响了。代码如下:

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

如果使用 promise ,那么我们可以使用如下写法:

for(var i = 0;i<10;i++){
  new Promise((resolve,reject)=>{
    setTimeout(resolve(console.log(i)),1000);
  });
}

执行效率上的考虑:

console.time('a');
for(let i = 0;i<10;i++){
  setTimeout(function(){
    console.log(i);  
  },1000);
}
console.timeEnd('a');
console.time('b');
for(var i = 0;i<10;i++){
  (function(i){
    setTimeout(function(){
      console.log(i);  
    },1000);
  })(i)
}
console.timeEnd('b');
console.time('c');
for(var i = 0;i<10;i++){
  new Promise((resolve,reject)=>{
    setTimeout(resolve(console.log(i)),1000);
  });
}
console.timeEnd('c');

效率上,我在目前新版 chrome 的执行结果如下:

a: 0.228ms
b: 0.203ms
c: 5.863ms

注意:可能不同系统不同环境的执行结果不一样。所以,有时候极客玩玩就是了,认真你就输啦-.-

总结

以前我偏执的认为,能做出东西就行了。基础不重要。所以我总是一只停留在做东西的阶段。但是后来反过来想:假设你想买车,那么你会选一个对汽车参数很熟的销售员还是选择一个你问什么问题他都说查查再回答你的销售员呢?答案是肯定的。所以说,可能公司招人也一样吧。

但是,这些问题真的有用吗?没错,我都有很多问题没回答好,如果是为了准备一次很 nice 的面试,我大可以特意的多跑几家,然后把遇到的有问题的面试题都总结下来。不会的再去查询,我想翻来覆去也没多少基础可问吧?就像找个美工就问:你会切图嘛?这些稍微花点时间都能学会的问题。个人觉得没特别大的意义。

(本文摘取自我的博客: http://79px.com/blog/57cd347be61eaab312faec27

2131 次点击
所在节点    前端开发
11 条回复
ChiangDi
2016-09-13 11:32:12 +08:00
这个问题比学会切图简单太多了
jiongxiaobu
2016-09-13 12:17:20 +08:00
好厉害啊 能写出那么多字
ericls
2016-09-13 12:19:15 +08:00
了解 call stack 即可!
xcodebuild
2016-09-13 13:21:02 +08:00
因为 var 是函数作用域的原因
j4fun
2016-09-13 13:53:26 +08:00
setTimeout 主要不是用在这种地方的。。主要是用在需要同步转异步的地方,可以用 setTimeout(func(){...}, 0) 好几年不写 js 了><,感觉这个变的好快,有时候看新的标准都有点看不懂了~
learnshare
2016-09-13 13:59:52 +08:00
并不是说没有用,写的时候一般都坑了自己。
zi
2016-09-13 14:08:33 +08:00
第一个用闭包就好了
for(var i = 0;i<10;i++){
setTimeout(function(i){
console.log(i);
}(i),1000);
}
YuJianrong
2016-09-14 10:30:03 +08:00
喂喂,这“探索”都出错了啊,先整理下自己的思路吧。

Promise 那个,

for(var i = 0;i<10;i++){
new Promise((resolve,reject)=>{
setTimeout(resolve(console.log(i)),5000);
});
}

因为 Promise 没有被使用,所以 Promise 的 func 直接运行, resolve 的返回值是 undefined, 所以 setTimeout 相当于 call 了
setTimeout(undefined, 1000);

所以总的来说这代码等价于:

for(var i = 0;i<10;i++){
console.log(i);
}

LZ 你还要好好学学 Promise 怎么工作啊……
YuJianrong
2016-09-14 10:30:53 +08:00
@zi 你这个错误和我上面说的一样。
zi
2016-09-14 10:46:17 +08:00
@YuJianrong 这错误我又犯了。。
这才对。。这样的话跟楼主第二个一样
setTimeout(function(i){
return function(){
console.log(i);
};
}(i),1000);
wesleypursuit
2016-09-14 14:27:41 +08:00
@YuJianrong 好吧,感谢提醒。 promise 本来不是很熟 这个写法是我写完博客后又有群友提供的,当时也有怀疑。但是只执行了下看结果是对的就贴上了。这么解释下就明白了-.-

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/305884

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX