您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# setTimeout与循环闭包的示例分析
## 引言
在JavaScript异步编程中,`setTimeout`与循环闭包的组合常会引发令人困惑的现象。本文将通过多个代码示例,深入分析这种组合产生的问题及其解决方案。
## 一、基础概念回顾
### 1.1 setTimeout的工作原理
```javascript
setTimeout(callback, delay)
callback
函数加入任务队列delay
毫秒后执行(最小延迟4ms)function outer() {
let count = 0;
return function inner() {
return ++count;
}
}
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
// 输出:6 6 6 6 6(每秒一个)
i
最终值为6i
的引用i
值// 实际执行等价于
var i;
for (i = 1; i <= 5; i++) {
// ...
}
时间点 | i值 | 内存状态 |
---|---|---|
t=0ms | 6 | 5个定时器已注册 |
t=1000ms | 6 | 第一个回调执行 |
… | … | … |
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, j * 1000);
})(i);
}
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
for (var i = 1; i <= 5; i++) {
setTimeout(function(j) {
console.log(j);
}, i * 1000, i);
}
for (var i = 1; i <= 3; i++) {
for (var j = 1; j <= 3; j++) {
setTimeout(function() {
console.log(i, j);
}, (i * 3 + j) * 100);
}
}
// 输出:4 4(共9次)
解决方案:
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
setTimeout(function() {
console.log(i, j);
}, (i * 3 + j) * 100);
}
}
const tasks = [];
for (var i = 0; i < 5; i++) {
tasks.push(() => {
return new Promise(res => {
setTimeout(() => res(i), 1000);
});
});
}
// 所有Promise都resolve(5)
优化方案:
const tasks = Array(5).fill().map((_, i) =>
() => new Promise(res => {
setTimeout(() => res(i + 1), 1000 * (i + 1));
})
);
方案 | 每次迭代内存消耗 | 总内存消耗 |
---|---|---|
var | 1个闭包 | 低 |
IIFE | n个闭包 | 中 |
let | n个块作用域 | 中 |
// 测试代码示例
console.time('let');
for (let i = 0; i < 10000; i++) {
setTimeout(() => {}, 0);
}
console.timeEnd('let');
典型结果: - let方案:~120ms - IIFE方案:~150ms - var方案:~80ms(但结果错误)
let
+块级作用域for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
while(true) {} // 阻塞测试
}, 1000);
}
async function process() {
for (let i = 1; i <= 5; i++) {
await new Promise(res => {
setTimeout(() => {
console.log(i);
res();
}, 1000);
});
}
}
通过本文分析可见,理解闭包与异步执行的交互机制至关重要。选择适合的解决方案需要权衡: - 代码可读性 - 运行环境要求 - 性能需求 - 团队协作约定
正确运用这些知识,可以避免常见的陷阱,编写出更健壮的异步代码。
附录:完整测试代码
// 所有方案的完整实现对比
const implementations = [
{
name: 'Problematic var',
code: () => {
for (var i = 1; i <= 5; i++) {
setTimeout(() => console.log('var:', i), i * 300);
}
}
},
{
name: 'IIFE Solution',
code: () => {
for (var i = 1; i <= 5; i++) {
(j => {
setTimeout(() => console.log('IIFE:', j), j * 300);
})(i);
}
}
},
{
name: 'let Solution',
code: () => {
for (let i = 1; i <= 5; i++) {
setTimeout(() => console.log('let:', i), i * 300);
}
}
}
];
implementations.forEach(impl => {
console.log(`\nRunning ${impl.name}:`);
impl.code();
});
”`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。