Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

死循环===栈溢出? #84

Open
wuyunqiang opened this issue Oct 28, 2024 · 0 comments
Open

死循环===栈溢出? #84

wuyunqiang opened this issue Oct 28, 2024 · 0 comments

Comments

@wuyunqiang
Copy link
Owner

先有问题再有答案

  1. 什么是死循环?
  2. 死循环有什么影响?
  3. js中实现死循环的方式有几种?
  4. 死循环页面一定会卡死嘛?
  5. 死循一定会导致栈溢出嘛?
  6. 死循环代表代码错误嘛?
  7. 无限循环有哪些应用场景?

35edd4dcee17eeb405aaa19a350cc866_512_512.jpg

死循环

在JavaScript中,死循环是指一种无法自行结束的循环。一般来说,循环会在满足某种条件时自动结束,比如for循环会在循环次数到达一定数量时结束,while循环会在条件不再满足时结束。但是,如果循环的结束条件没有被正确地设计或实现,那么循环可能永远无法自行结束,这就是死循环。

for(let i = 0; i >= 0; i++){ // 这里是循环体 }

上面的例子中,i的初始值为0,并且在每次循环执行后,i的值都会增加1,那么i的值永远都会大于等于0,因此,这个for循环的结束条件i >= 0永远无法满足,这是一个死循环。

while(true){ // 这里是循环体 }

在上面的例子中,由于while循环的条件直接写成了常量true,所以它永远满足,导致while循环无法自行结束,这是一个死循环。

死循环的影响:

  1. 用户体验差:JavaScript是运行在浏览器的,当一个死循环运行时,它将使得浏览器无法响应用户的交互行为,导致页面卡死,生成“页面无响应”的错误。
  2. CPU占用过高:死循环将导致CPU满载运转,这将严重拖慢计算机的运行速度,甚至导致计算机无法正常运行。
  3. 内存消耗增加:如果在循环中进行大量数据的处理或者对象的创建,可能会导致内存消耗过大,甚至出现内存泄漏,造成浏览器崩溃。
  4. 电能消耗:长时间运行死循环会使得计算机无法进入待机状态,引起不必要的电能消耗。
  5. 代码的健壮性:程序代码出现死循环,通常是程序设计上的错误,表明代码的健壮性、鲁棒性有待提高。

因此在编写JavaScript代码时,需要避免死循环的产生,保证循环有明确的退出条件。必要时可以设置迭代限制,防止可能的死循环。

死循环的N种方式

通常来说,死循环的存在往往是程序设计上的错误,并没有优点。因为它会浪费系统资源,占用大量的CPU性能,甚至可能导致系统崩溃。

然而,在某些特定的情况下,我们可能会使用类似死循环的能力。

分帧动画

let xPos = 0;
function loop() {
  xPos += 5; // 改变位置

  if (xPos > window.innerWidth) {
    xPos = 0;
  }

  // 改变元素位置
  document.getElementById("myElement").style.left = xPos + "px";

  requestAnimationFrame(loop); // 在下一个刷新周期调用loop
}

// 在下一个刷新周期调用loop
requestAnimationFrame(loop);

上述代码,requestAnimationFrame(loop)使得loop函数将在下一次界面刷新时被调用,并且它在每次调用完毕后又调用了自身,建立了一个持续的刷新循环。

requestAnimationFrame的优点是它会在浏览器下个刷新周期自动调用,与浏览器绘制的频率同步,可以保证动画的流畅性,并且当页面不可见或最小化时,动画会自动暂停,可以有效节省CPU开销和电能。

关于详细的requestAnimationFrame介绍 可以参考这里 浏览器: 深入理解requestAnimationFrame优化js运行时

与标准的“死循环”不同,这个模式可以被浏览器控制,是在动画或游戏开发中常见的工作模式,是一种被提倡的动画开发方式之一.

计时器循环

let timer;
function loop() {
    if(timer){
        clearTimeout(timer)
    }
    // 执行一些业务逻辑
    timer = setTimeout(loop, 1000);
}
loop()

这段代码实现了一个死循环。它通过递归地设置一个 setTimeout 来不断调用 loop 函数,从而形成一个无限循环。每次 loop 被调用时,它都会清除上一个定时器(如果存在的话),然后设置一个新的定时器,1秒后再次调用 loop 函数。

这种类型的循环通常用于以下应用场景:

  1. 定时任务:定期执行某些操作,比如每隔一定时间检查服务器的状态、更新UI元素、发送心跳信号等。
  2. 长轮询:在WebSocket不可用的情况下,作为一种与服务器保持通信的方式,客户端定期发送请求到服务器,服务器在有更新时立即响应
  3. 动画帧控制:在之前的动画制作中,大家对requestAnimationFrame还不太了解 大部分是通过计时器定期更新画面来创建平滑的动画效果。
  4. 资源释放:在某些情况下,可能需要定期检查和释放不再使用的资源。
  5. 监控需要:用于实现监控数据的收集。在监控系统中,通常需要定期收集系统或应用程序的性能指标、日志信息、资源使用情况等数据。使用 setTimeout 来实现循环可以确保这些数据在固定的时间间隔内被收集。

Generator循环

function* trafficLight() {
    while (true) {
        yield { color: 'green', duration: 5000 };
        yield { color: 'yellow', duration: 3000 };
        yield { color: 'red', duration: 2000 };
    }
}

const light = trafficLight();

function changeLight() {
    const state = light.next().value;
    console.log(state.color);
    setTimeout(changeLight, state.duration);
}

changeLight();

这段代码是利用JavaScript的Generator函数和yield关键词来模拟一个交通灯的逻辑。

Generator函数的特性是可以暂停和恢复执行,yield关键字可以让Generator函数暂停执行并返回一个值,再次调用next方法时又从上次暂停的地方恢复执行。

这段代码trafficLight函数内部有一个无限循环,模拟了交通灯不停变化的过程。每次循环都依次返回一个对象,代表了交通灯的状态(颜色)和持续的时间。

  1. 第一次调用trafficLight().next()之后,遇到第一个yield,返回一个对象{ color: 'green', duration: 5000 },交通灯变为绿色,持续时间为5000毫秒。
  2. 第二次调用trafficLight().next()时,会从第一个yield后面继续执行,直到遇到第二个yield,返回一个对象{ color: 'yellow', duration: 3000 },交通灯变为黄色,持续时间为3000毫秒。
  3. 第三次调用trafficLight().next()时,继续执行,遇到第三个yield,返回一个对象{ color: 'red', duration: 2000 },交通灯变为红色,持续时间为2000毫秒。
  4. 第四次调用trafficLight().next()时,因为是在循环中,所以会再次从头开始执行,返回绿灯的状态。

所以,这段代码的逻辑就是模拟了一个交通灯颜色不断轮换的过程,绿灯→黄灯→红灯→绿灯→黄灯→红灯......,并且每种颜色的灯持续的时间也是固定的。

更多关于Generator的高级用法请看这篇文章 js核心之Generator函数

同步、异步死循环

 function btn() {
        console.log('test btn');
        // btn(); 第一种情况 同步死循环
        // setTimeout(btn,0) 第二种情况 异步宏任务死循环
        // Promise.resolve().then(btn) 第三种情况 异步微任务死循环
       
    }
btn();

这部分的内容可以看这篇文章 浏览器:帧&事件循环 已经讲的很详细了 。

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant