Is there a way to "break" when a value changes?

VS Code - html runs a .js. script.

I have one value that changes when it should not change. Is there a way to set a break when it changes.

This is too vague for us to give a response.

Give us the code you are using, tell us what the user should do, and what the code should do in response. Something like that. Spell out what the “change” is.

If you know where the change is occurring, this is possible. E.g:

let foo = 10;

function changeFoo() {
  foo += 1;
}

setTimeout(changeFoo, 2000);

In this scenario you can set a conditional breakpoint.

  1. Place a breakpoint on the line foo += 1; (right click > add breakpoint)
  2. Right-click on the breakpoint symbol (red dot) next to the line number.
  3. Select “Edit Breakpoint…”.
  4. In the condition field, enter foo !== 10

Now, VS Code will pause execution when foo changes from its initial value.

If the file is long and you don’t know where the variable change takes place, then things get more tricky. AFAIK, VS Code does not natively support breakpoints that trigger when a specific variable changes.

You could hack around this by using a proxy, but this requires refactoring foo to be an object.

let foo = { value: 10 };

const fooProxy = new Proxy(foo, {
  set(target, property, value) {
    console.trace(`foo.${property} changed from ${target[property]} to ${value}`);
    target[property] = value; 
    return true;
  }
});

function changeFoo() {
  fooProxy.value += 1;
}

setTimeout(changeFoo, 2000);

In this code, a Proxy is created to wrap foo, and a custom set handler is defined to log a stack trace whenever foo.value is changed. This way, you can see exactly where in your code foo.value is being modified. E.g.:

~/Desktop  node index.js 
Trace: foo.value changed from 10 to 11
    at Object.set (/home/jim/Desktop/index.js:4:17)
    at Timeout.changeFoo [as _onTimeout] (/home/jim/Desktop/index.js:11:20)
    at listOnTimeout (node:internal/timers:559:17)
    at processTimers (node:internal/timers:502:7)
2 Likes

This is a segment of the code:

//------------------------------
const gcCanvas = document.getElementById('canvas');
  gcCanvas.width = 600;
  gcCanvas.height = 300;

const gcContext = gcCanvas.getContext('2d');

const gcBackgroundImage = new Image();
const gcBackgroundWidth = gcCanvas.width;
let glBackgroundHeight; 

const gcCarImage = new Image();
const gcCarImageWidth = 40; 
const gcCarImageHeight = 100; 
let glCarTopLeftX; 
let glCarTopLeftY; 

let glDegrees = 0;
let glPotentialDegrees = 0;
let glArrowFlag = false;
let glRotateFlag = false;
let glStartAndStopTimeoutID;
//------------------------------
//++++++++++++++++++++++++++++++
//------------------------------
document.addEventListener('DOMContentLoaded', (ev) => 
  {
    gcBackgroundImage.onload = function ()
    {
      gcCarImage.onload = function () 
      {
        const cAspectRatio = gcBackgroundImage.naturalWidth / gcBackgroundImage.naturalHeight;
        glBackgroundHeight = gcCanvas.height = gcBackgroundWidth / cAspectRatio; 
        gcContext.drawImage(gcBackgroundImage, 0, 0, gcBackgroundWidth, glBackgroundHeight); 
        glCarTopLeftX = gcBackgroundWidth / 2 - gcCarImageWidth / 2; 
        glCarTopLeftY = glBackgroundHeight / 2 - gcCarImageHeight / 2; 
        gcContext.drawImage(gcCarImage, glCarTopLeftX, glCarTopLeftY, gcCarImageWidth, gcCarImageHeight);
      }
      gcCarImage.src = "Car.jpg"; 
    }
    gcBackgroundImage.src = "Streets.jpg";
  }
)//document.addEventListener('DOMContentLoaded'
//------------------------------
//------------------------------
document.body.addEventListener("keydown", e =>
  // the "keydown" event fires for all keybpoard entries.
  {
    e.preventDefault();
    if(glRotateFlag === false){
      switch(e.key) {
         default:
          break;
        case 'r':
          glRotateFlag = true;
          fRotate();
          break;
        case '-':
          glPotentialDegrees = '-';
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          if(glPotentialDegrees === 0) {
            glPotentialDegrees = '';
          }
          glPotentialDegrees += e.key;
          let lAbs = Math.abs(glPotentialDegrees)
          if(lAbs >= 360) {
            glPotentialDegrees = e.key;
          }
          else if(lAbs === 360) {
            glPotentialDegrees = 0;
          }
          break;
      }// switch(e.key)  
    }// if(glRotateFlag
  }// =>
)// document.body.addEventListener("keydown"
//------------------------------
//------------------------------
function fRotate() 
{
  if(glRotateFlag === true){
    lCarCenterX = glCarTopLeftX + gcCarImageWidth / 2;
    lCarCenterY = glCarTopLeftY + gcCarImageHeight / 2;
    if(glDegrees < glPotentialDegrees){
      let lRotateRight = () => {
        fMove(lCarCenterX, lCarCenterY, glDegrees);
        if(glDegrees < glPotentialDegrees) {
          glDegrees += 1;
          glStartAndStopTimeoutID = setTimeout(lRotateRight, 10);
        }
        else {
          clearTimeout(glStartAndStopTimeoutID);
          glPotentialDegrees = '';
          glRotateFlag = false;
        }
      };
      lRotateRight();  
    }
    else if(glDegrees > glPotentialDegrees){
      let lRotateLeft = () => {
        fMove(lCarCenterX, lCarCenterY, glDegrees);
        if(glDegrees > glPotentialDegrees) {
          glDegrees -= 1;
          glStartAndStopTimeoutID = setTimeout(lRotateLeft, 10);
        }
        else {
          clearTimeout(glStartAndStopTimeoutID);
          glPotentialDegrees = '';
          glRotateFlag = false;
        }
      };
      lRotateLeft();  
    }
    else {
      glPotentialDegrees = '';
      glRotateFlag = false;  
    }
  }    
}// fRotate
//------------------------------
//------------------------------
function fMove(lCarCenterX, lCarCenterY, glDegrees) 
{
  gcContext.clearRect(0, 0, gcCanvas.width, gcCanvas.height);
  gcContext.drawImage(gcBackgroundImage, 0, 0, gcBackgroundWidth, glBackgroundHeight);
  let lRadians = Math.PI / 180 * glDegrees;
  gcContext.translate(lCarCenterX, lCarCenterY);
  gcContext.rotate(lRadians);
  gcContext.drawImage(gcCarImage, -gcCarImageWidth / 2, -gcCarImageHeight / 2, gcCarImageWidth, gcCarImageHeight);
  gcContext.rotate(-lRadians);
  gcContext.translate(-lCarCenterX, -lCarCenterY);    
}// fMove
//------------------------------

Streets.jpg => https://snipboard.io/4YgDaf.jpg
Car.jpg => https://snipboard.io/nubKPj.jpg

The car rotates to a specified angle. Enter 45 then enter ‘r’ → the car rotates to 45 degrees. Enter -22 then enter ‘r’ → the car rotates to -22 degrees.

Sometimes it returns to 0 degrees.

This is only a segment. If requested, I could post all of the code.

I dont know why you’re calling clearTimeout in the resolution of the timeout… but whatever…

are you able to reliably reproduce the error?

I think I’ve found the problem. I have multiple “setTimeout(…)'s”.

I had forgotten “clearTimeout(…)” after every “setTimeout(…)” was finished. This resulted in erratic behavior.

Debugging is not a feature of the language. Debugging software is separate. All major browsers have a debugger; are you asking about use of that debugger only or are you asking about other debuggers?

When I search Google for javascript break when a value changes I get many possibilities.

Someone says that Firebug for Firefox has the feature.

Someone else says you could redefine the property with an accessor.

Someone suggested object.watch polyfill in ES5.

Firebug for Firefox no longer exists.

1 Like

Yeah the major browsers are shipping with quite capable dev tools by themselves these days. :-) Here’s on conditional breakpoints specifically for Chrome and FF. And you can actually execute any one-liner code in the condition, as for instance setting global variables for inspection or use in other breakpoints.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.