• js 死循环代码debug


    最近在学习vuejs设计与实现的编译器

    忧虑

    在代码里面有大量的while这样的状态机,一不小心就会出现死循环。这让我非常的忧虑。


    我当下的问题是:

    • 如何在遇到死循环时候,及时中断
    • 如何在遇到是循环的时候,可以定位到时那个函数出问题
    • 如何在不影响原代码【逻辑】的情况下(非不新增代码,而是不影响原来代码的逻辑),中断改死循环

    我在想一个问题

            能否可以捕获到【死循环】。在检测到有死循环的时候,主动中断程序,并报告当前的死循环的程序的函数的名字。

            这里的捕获是在不影响原代码的情况下,可以监听到死循环,并主动中断,并暴露死循环的函数名字。

            当时经过几次尝试,例如给程序再包裹一个函数或注入代码,我发现好像都有些不大如意的地方。

            嗯,那如果抽离这个检测死循环的代码到独立函数,且让有while的程序自己调用,是否就可以一定层度的减少心智压力以及更好的debug呢?

    函数名字

    首先,我们需要得到运行函数的名字,js并没有提供这样的功能,但是函数的arguments.callee却可以拿到整个函数内容的文本

    例如

    1. function a(){
    2. //dosome
    3. console.log(arguments.callee.toString())
    4. }

    执行代码a()后打印出来的内容是

    得到的就是字符串

    'function a(){
    //dosome
     console.log(arguments.callee.toString())
    }'

    弊端

    但是这有个弊端,如果代码是匿名函数,就拿不到函数名字,例如下面这样的代码,虽然复制给了变量a,但是函数本身是匿名函数

    1. var a=function (){
    2. //dosome
    3. console.log(arguments.callee.toString())
    4. }

    因为函数是个匿名函数,得到的字符串如下,这种情况就需要用户自己传递过来了

    'function(){
    //dosome
     console.log(arguments.callee.toString())
    }'

    正则

    那我们可以使用正则拿到函数名字

    1. let str = arguments.callee.toString();
    2. var re = /^function\s*(\w+)\(/ig;
    3. var matches = re.exec(str);
    4. let fnName = matches[1];



    死循环的特征是什么?


    一个函数一直的不断的执行!!

    如何判断呢?


    如果在n秒内不断的执行某个函数那就可以认为他是死循环
    我们可以用两个时间的间隔来判断这个函数执行了多久,如果大于某个时间,就让他break;


    分解


    那确定了方案后开始,对这个抽离的函数进行分解

    • 需要维护开始时间,结束时间
    • 需要更新结束时间
    • 需要为何死亡时间
    • 需要提供判断是否时死循环的函数


    实现代码如下:
     

    1. var deadLoopCatch = function(argument, runFnName, deadLoopTimeLimit = 5000) {
    2. let fnName;
    3. if (runFnName) {
    4. fnName = runFnName;
    5. } else {
    6. let str = argument.callee.toString();
    7. var re = /^function\s*(\w+)\(/ig;
    8. var matches = re.exec(str);
    9. if (matches && matches[1]) {
    10. fnName = matches[1];
    11. } else {
    12. //如果是匿名函数,且用户没有传递第二个参数的时候,警告
    13. console.warn("When the loop is an anonymous function, please pass the second parameter for function deadLoopCatch")
    14. fnName = "---UMKNOW--";
    15. }
    16. }
    17. //初始化开始时间和结束时间
    18. let startTime = endTime = new Date().getTime();
    19. let result = {
    20. fnName,
    21. _startTime: startTime,
    22. _endTime: endTime,
    23. _deadLoopTimeLimit: deadLoopTimeLimit,
    24. updateTime: () => {
    25. result._endTime = new Date().getTime();
    26. },
    27. isDeadLoop: () => {
    28. let isDeadLoop = result._endTime - result._startTime >= result._deadLoopTimeLimit;
    29. //这代码好像有一点弊端,如果这里不执行的话,还是会死循环
    30. console.log("deadLoopCatch:result._endTime - result._startTime", result._endTime - result._startTime)
    31. if (isDeadLoop) {
    32. console.error(`Function ${fnName} triggered an endless loop!`)
    33. }
    34. return isDeadLoop;
    35. }
    36. }
    37. return result;
    38. }

    一般情况下while不会执行耗时操作,所以如果一个while执行超过5秒,我们认为他就是死循环了,当然这个时间可以根据自己的实际情况调整


    代码使用:

    1. var abc = function(a, b, c) {
    2. var i = 1;
    3. let deadLoopCatchInit = deadLoopCatch(arguments, 'abc', 1000);
    4. while (true) {
    5. deadLoopCatchInit.updateTime()
    6. if (deadLoopCatchInit.isDeadLoop()) {
    7. console.warn("强制中断", deadLoopCatchInit)
    8. break;
    9. }
    10. console.log("执行中....")
    11. }
    12. }
    13. abc()

    另外一个方案

            另外个简单debug方式,就是在while第一行使用console.log(“函数名字”) 这种方式也可以定位到死循环是哪一个代码,但是却无法避免死循环。

    疑问点,console

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>Document</title>
    8. </head>
    9. <body>
    10. <p>为什么去掉地46行,死循环拦截就没效果了!!<span id="txt"></span></p>
    11. <script>
    12. var deadLoopCatch = function(argument, runFnName, deadLoopTimeLimit = 5000) {
    13. let fnName;
    14. if (runFnName) {
    15. fnName = runFnName;
    16. } else {
    17. let str = argument.callee.toString();
    18. var re = /^function\s*(\w+)\(/ig;
    19. var matches = re.exec(str);
    20. if (matches && matches[1]) {
    21. fnName = matches[1];
    22. } else {
    23. //如果是匿名函数,且用户没有传递第二个参数的时候,警告
    24. console.warn("When the loop is an anonymous function, please pass the second parameter for function deadLoopCatch")
    25. fnName = "---UMKNOW--";
    26. }
    27. }
    28. //初始化开始时间和结束时间
    29. let startTime = endTime = new Date().getTime();
    30. let result = {
    31. fnName,
    32. _startTime: startTime,
    33. _endTime: endTime,
    34. _deadLoopTimeLimit: deadLoopTimeLimit,
    35. updateTime: () => {
    36. result._endTime = new Date().getTime();
    37. },
    38. isDeadLoop: () => {
    39. let isDeadLoop = result._endTime - result._startTime >= result._deadLoopTimeLimit;
    40. //这代码好像有一点弊端,如果这里不执行的话,还是会死循环
    41. // console.log("deadLoopCatch:result._endTime - result._startTime", result._endTime - result._startTime)
    42. if (isDeadLoop) {
    43. console.error(`Function ${fnName} triggered an endless loop!`)
    44. }
    45. return isDeadLoop;
    46. }
    47. }
    48. return result;
    49. }
    50. window.onload = function() {
    51. const dom = document.querySelector("#txt")
    52. var abc = function(a, b, c) {
    53. var i = 1;
    54. let deadLoopCatchInit = deadLoopCatch(arguments, 'abc', 1000);
    55. while (true) {
    56. deadLoopCatchInit.updateTime()
    57. if (deadLoopCatchInit.isDeadLoop()) {
    58. console.warn("强制中断", deadLoopCatchInit)
    59. dom.innerHTML = `更新第(${i}次)后<b style='background:red;'>强制中断</b>了`
    60. break;
    61. }
    62. i++;
    63. dom.innerHTML = `更新第(${i}次)`
    64. console.log("执行中....")
    65. }
    66. }
    67. abc()
    68. }
    69. </script>
    70. </body>
    71. </html>

    运行一下上面的代码后会得到个奇怪的线下console.log的运行会滞后,while已经结束了,html已经输出了“强制中断”,console打印还在不断的追赶

    console延后执行了



     

    貌似console是个微任务的赶脚,这一定程度上也说的通,因为本来是用来调试的,不堵塞住流程代码也是应该的。

  • 相关阅读:
    C++中tuple数据结构使用
    【UE 粒子练习】03——创建一个GPU粒子
    Docker安装MongoDB
    面试官:设计模式中的原型模式是什么?
    使用Wesky.Net.Opentools库,一行代码实现自动解析实体类summary注释信息(可用于数据实体文档的快速实现)
    尚硅谷尚品项目汇笔记(三)
    2024年CSP-J暑假冲刺训练营(1):枚举
    spring技术体系的演进史
    解决ios17无法复制的问题
    机器翻译目前广泛应用于文档翻译以及硬件翻译
  • 原文地址:https://blog.csdn.net/u014071104/article/details/127033594