Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Javascript/Workers not refreshing

I'm seeing this weird issue where changing or even commenting out a worker doesn't change or break the function that worker was handling.  Is there a way I can force the workers to be reloaded?  I'm sure there are posts on this, but I haven't found them.
1586941183
Andreas J.
Forum Champion
Sheet Author
Translator
Are you working in the Sheet Sandbox, or in a campaign with a "custom" sheet? Could you elaborate on what you mean with "changing or even commenting out a worker doesn't change or break the function"? Could you describe what your sheetworker was supposed to do first, and what it's meant to do after your update?
1586963849

Edited 1586964164
The sheetworker looks for changes a certain field.  When it sees a change, it takes the new value from that field, finds it in an array, then finds a corresponding value in the same array, and uses that value to update a different field that is tied to the first. For instance, if you change how much experience you have, it will update what your current level is. I am working in Sheet sandbox and with a custom character sheet. The issue is that I have a worker that was working correctly, but was inefficient. So I changed it to be more efficient and then saved the file, loaded it into the sheet sandbox, and tested the worker. I did not see evidence that the new code was being used. I tried closing and opening the character sheet, refreshing the browser window, and closing and opening the sheet sandbox. Nothing made a difference.  I entered incorrect values into the array that the sheet worker references, to make sure that I was seeing the output of that modified sheet worker, but the output I saw was still correct. I tried commenting out the worker entirely, but the worker continued to function. I'm using Windows 10 and chrome if that makes a difference.
1586964397
GiGs
Pro
Sheet Author
API Scripter
Descriptions of code are great for telling us what is meant to happen, but we cant diagnose code problems without seeing the actual code. Though the problem you describe sounds to me like you are loading the wrong file into the saandbox. Do you have an older copy of the file with the original code in it? This isnt a silly question - I've done this myself.
1586986176

Edited 1586987110
No, it's not a silly question.  However, I edit my files using notepad++ and save them to and load them from my Google drive.  So, while I only ever have one copy at a time, I also have version control. This issue is just weird.  It didn't seem to me like it could be an issue with the code, but then again, I wrote the code at like 3am, and I've never used a while loop in Roll20 before.  I suppose it's possible that the code uses unsupported elements and is therefore rejected outright, and the older version is used.  After all, Javascript is designed to tolerate errors and keep on chugging.  Plus, there could also be something wrong with my code.  So here it is: <div class="clevel blueBackground blueBorderRight1 section"> <div class="fieldLabel white section">Level</div> <input name="attr_current_level" type="hidden"> <span class="field derived" name="attr_current_level"></span> </div> <div class="experi blueBackground section"> <div class="fieldLabel white section">Experience</div> <input class="field manual23" name="attr_experience" type="number" min='0'> </div> <div class="nxtlvl blueBackground section"> <div class="fieldLabel white section">Next Level</div> <input name="attr_next_level" type="hidden"> <span class="field derived" name="attr_next_level"></span> </div> <script type="text/worker"> const int = score => parseInt(score) || 0; on('change:experience', () => { getAttrs(['experience'], values => { const exp = int(values.experience); const exp_table = [ /* 0 <= */ -1, /* 1 <= */ 1500, /* 2 <= */ 4500, /* 3 <= */ 13500, /* 4 <= */ 25500, /* 5 <= */ 40500, /* 6 <= */ 58500, /* 7 <= */ 79500, /* 8 <= */ 103500, /* 9 <= */ 130500, /* 10 <= */ 160500, /* 11 <= */ 193500, /* 12 <= */ 229500, /* 13 <= */ 268500, /* 14 <= */ 310500, /* 15 <= */ 355500, /* 16 <= */ 403500, /* 17 <= */ 454500, /* 18 <= */ 508500, /* 19 <= */ 565500, /* 20 <= */ 625500, /* 21 <= */ 688500, /* 22 <= */ 754500, /* 23 <= */ 823500, /* 24 <= */ 895500, /* 25 <= */ 970500, /* 26 <= */ 1048500, /* 27 <= */ 1129500, /* 28 <= */ 1213500, /* 29 <= */ 1300500, /* 30 <= */ 1390500 ]; var firstIndex = 1; var lastIndex = exp_table.length; var middleIndex = Math.floor((lastIndex + firstIndex)/2); console.log( 'firstIndex: ' + firstIndex +'\n', 'middleIndex: ' + middleIndex +'\n', 'lastIndex: ' + lastIndex ); while(exp_table[middleIndex] != exp && (firstIndex - lastIndex) > 1) { console.log( 'firstIndex: ' + firstIndex +'\n', 'middleIndex: ' + middleIndex +'\n', 'lastIndex: ' + lastIndex ); if (exp < exp_table[middleIndex]) { lastIndex = middleIndex - 1; } else if (exp > exp_table[middleIndex]) { firstIndex = middleIndex + 1; } middleIndex = Math.floor((lastIndex + firstIndex)/2); } const cur_level = ((firstIndex < middleIndex) ? firstIndex : middleIndex); const nxt_level = exp_table[cur_level]-exp; console.log( 'current_level: ' + cur_level +'\n', 'experience: ' + exp +'\n', 'next_level: ' + nxt_level +'\n', 'until_next_level: ' + nxt_level-exp ); setAttrs({ current_level: cur_level, next_level: nxt_level }); }); }); </script>
1586991663
GiGs
Pro
Sheet Author
API Scripter
Your while loop is pretty complex. There could be an issue in matching certain values there, but I'd have to play aorund to test it. But there's a quicker way to do that sheet worker using findIndex.  on('change:experience', () => {     getAttrs(['experience'], values => {         const exp = int(values.experience);         const exp_table = [ /* 0 <= */  -1, /* 1 <= */  1500, /* 2 <= */  4500, /* 3 <= */  13500, /* 4 <= */  25500, /* 5 <= */  40500, /* 6 <= */  58500, /* 7 <= */  79500, /* 8 <= */  103500, /* 9 <= */  130500, /* 10 <= */ 160500, /* 11 <= */ 193500, /* 12 <= */ 229500, /* 13 <= */ 268500, /* 14 <= */ 310500, /* 15 <= */ 355500, /* 16 <= */ 403500, /* 17 <= */ 454500, /* 18 <= */ 508500, /* 19 <= */ 565500, /* 20 <= */ 625500, /* 21 <= */ 688500, /* 22 <= */ 754500, /* 23 <= */ 823500, /* 24 <= */ 895500, /* 25 <= */ 970500, /* 26 <= */ 1048500, /* 27 <= */ 1129500, /* 28 <= */ 1213500, /* 29 <= */ 1300500, /* 30 <= */ 1390500         ];         const nxt_level = exp_table.findIndex(element => element > exp) || 31;         const cur_level = nxt_level - 1;         setAttrs({             current_level: cur_level,             next_level: nxt_level         });     }); }); If this fixes your issue we know the code is being loaded properly. I added ||31 on the nxt_level to account for the situation where someone might enter experience outside of the accepted range.
Thank you so much GiGs!  I'm learning so much about Javascript = ).
1586994492
GiGs
Pro
Sheet Author
API Scripter
You're welcome. Looking again, I think the nextlvel and current level calculations might be one off. Maybe they should be:         const cur_level = exp_table.findIndex(element => element > exp) || 31;         const nxt_level = nxt_level + 1;
Unfortunately, as beautiful as this solution is, it throws a couple "int is not defined" errors, which don't make sense to me. ReferenceError: int is not defined     at Object.eval [as -M4XqUlMRJ2NOq-BS1NH//false//0.30268762950246053] (eval at messageHandler (sheetsandboxworker.js?1586994991586:698), <anonymous>:4:17)     at _fullfillAttrReq (sheetsandboxworker.js?1586994991586:673)     at messageHandler (sheetsandboxworker.js?1586994991586:705) sheetsandboxworker.js?1586994991586:734 ReferenceError: int is not defined     at Object.eval [as -M4XqUlMRJ2NOq-BS1NH//false//0.30268762950246053] (eval at messageHandler (sheetsandboxworker.js?1586994991586:698), <anonymous>:4:17)     at _fullfillAttrReq (sheetsandboxworker.js?1586994991586:673)     at messageHandler (sheetsandboxworker.js?1586994991586:705)
1586996323
GiGs
Pro
Sheet Author
API Scripter
That's saying the int function doesnt exist. Which is weird, because your code referred to it. Do you have more than one script block in your sheet? If you more than one section that starts like this <script type="text/worker"> you need to combine them all into one Create one script block either at the beginning or the end of the html page, and put all your sheet workers in that. Then make sure any functions that are called in your workers are at the start of the block. You probably have an int function in one of them that looks like this: const int = (value, error = 0) => parseInt(value) || error; Put that and any other functions like it at the start of the block, like so <script type="text/worker"> const int = (value, error = 0) => parseInt(value) || error; Then put all your sheet workers after it before the </script> line If that isnt what's causing your issue, put that int function in your script block anyway.
1587004423

Edited 1587004553
Wow! I learned quite a bit from that post.  I had a couple dozen functions, each with their own script block. I'd made a lot of progress on building up my java-script proficiency.  I was just starting to wonder what some of that other code was for.  For instance, I don't know what any of the following do: const int = (value, error = 0) => parseInt(value) || error ; on('change:experience', () => { getAttrs(['experience'], values => { I think it's mostly that I haven't wrapped my mind around "=>" yet.  I've looked at w3schools, and I have an idea.  It just hasn't crystallized in my head just yet. You were right about the issue with these lines:         const nxt_level = exp_table.findIndex(element => element > exp) || 31;         const cur_level = nxt_level - 1; However, one of your replacement lines had an issue too:         const nxt_level = nxt_level + 1; I think you meant:         const nxt_level =  cur_level  + 1; That said, I never really explained what I was going for.  The variable "nxt_level" is supposed to hold the amount of experience needed to get to the next level, so I replaced it with: const nxt_level = exp_table[cur_level]-exp; In addition, in the course of fixing this, I did some thorough examination through console.log(), and decided that I needed to remove all my functions and replace them one by one, testing each.  When I wrote them, I wasn't looking at console.log(), and there is an error thrown from one of them that I'm still looking for.  Every one I've put back so far is working correctly. The " || 31" still resulted in an error.  I tried to catch it, but that didn't work, so I used an if statement. Thanks again for your help = ). Here is the final code, in case anyone wants to see it: <div class="clevel blueBackground blueBorderRight1 section"> <div class="fieldLabel white section">Level</div> <input name="attr_current_level" type="hidden"> <span class="field derived" name="attr_current_level"></span> </div> <div class="experi blueBackground section"> <div class="fieldLabel white section">Experience</div> <input class="field manual23" name="attr_experience" type="number" min='0'> </div> <div class="nxtlvl blueBackground section"> <div class="fieldLabel white section">Next Level</div> <input name="attr_next_level" type="hidden"> <span class="field derived" name="attr_next_level"></span> </div> <script type="text/worker"> const int = (value, error = 0) => parseInt(value) || error; <!-- set current level and next level experience --> on('change:experience', () => { getAttrs(['experience'], values => { const exp = int(values.experience); const exp_table = [ /* 0 <= */ -1, /* 1 <= */ 1500, /* 2 <= */ 4500, /* 3 <= */ 13500, /* 4 <= */ 25500, /* 5 <= */ 40500, /* 6 <= */ 58500, /* 7 <= */ 79500, /* 8 <= */ 103500, /* 9 <= */ 130500, /* 10 <= */ 160500, /* 11 <= */ 193500, /* 12 <= */ 229500, /* 13 <= */ 268500, /* 14 <= */ 310500, /* 15 <= */ 355500, /* 16 <= */ 403500, /* 17 <= */ 454500, /* 18 <= */ 508500, /* 19 <= */ 565500, /* 20 <= */ 625500, /* 21 <= */ 688500, /* 22 <= */ 754500, /* 23 <= */ 823500, /* 24 <= */ 895500, /* 25 <= */ 970500, /* 26 <= */ 1048500, /* 27 <= */ 1129500, /* 28 <= */ 1213500, /* 29 <= */ 1300500, /* 30 <= */ 1390500 ]; var cur_level = "Yes!"; var nxt_level = "Apotheosis!"; if (exp < 1390500) { cur_level = exp_table.findIndex(element => element > exp); nxt_level = exp_table[cur_level]-exp; } console.log( 'current_level: ' + cur_level + '\n', 'next_level: ' + nxt_level ); setAttrs({ current_level: cur_level, next_level: nxt_level }); }); }); </script>
1587007544

Edited 1587007708
GiGs
Pro
Sheet Author
API Scripter
I'm glad you're finding the errors, those look like good changes to the code. The => syntax is part of recent additions to javascript that allows much more concise syntax. For example, these two functions do exactly the same thing: const int = (value, error = 0) => parseInt(value) || error ; Function int (value, error) {     if(!error) error = 0;     return parseInt(value) || error } In roll20, all attributes are stored as strings, or text. But you often want to treat them as numbers, so you can do arithmetic, or logical comparisons (is one number bigger than another). So, very frequently you'll want to convert an attribute to a number, and would usually do something like hits var exp = parseInt(values.experience) || 0; but typing that out gets tedious and its much easier to type var exp = int(values.experience); So I created the int function to save typing, so whenever you type int(), whatever is in the brackets gets passed to the function, and processed, then a number is returned. The longer int function (that starts with function(int) ) makes it a bit clearer whats happening, and is a very familiar format to anyone who has done programming, The shorter function looks a bit weird to most people, but it does the same thing, and that syntax gets more comfortable the more you use it. So the important concept here is when you have a function the brackets at the start () contain a function's parameters , the things you pass to that function for it to do things with. Not all functions have parameters but they still need the brackets. Take this line on('change:experience', () => { This is the same as on('change:experience', function () { the => is just telling us, this is a function and the () => { is basically saying - "this is a function, send whatever is in the brackets to the function body surrounded by {}" . It starts making sense when you realise the arrow is pointing to the function. So this line on('change:experience', function () { is telling us "when the experience attributes changes, run this function." and everything that follows inside the { } brackets is a function: its the sheet worker function. The first thing that happens after that, is we have another function being initiated: getAttrs(['experience'], values => { which is the same as this: getAttrs(['experience'], function( values) { This is the getAttrs function, and it says basically: grab the experience attribute from the character sheet and store it in an object called values. You can look at whats inside the object values by doing this console.log(values): and you'd see something in the console like this {experience: "1300"} Lets say you had done getAttrs(['experience'. 'level', 'class'], that values object would look like something this {     experience: "1300",     level: "1",     class: 'fighter' } That should be a familiar layout, since you see it when you use setAttrs, So, getAttrs grabs attribute names and values, and stores them in this object called values. But you want to do things with them, so you need to get them out of that object. So you do this var exp = values.experience; or var exp = values['experience']; Those two both do the same thing - the second one is a bit clunkier, but there are occasions where you have to use it - like in a couple of lines from now. So, you've seen this before. When you do, say  nxt_level = exp_table[cur_level]; you are using a very similar thing: using a key (cur_level) to get a value (the actual number) out of an object. In this case it's an array, but an array is just a simplified object.  And when you use setAttrs, notice it's a function: setAttrs() and inside the brackets you have an object: {     current_level: cur_level,     next_level: nxt_level } This is exactly the same format as the getAttrs object: you are packaging up the attribute names and their values into the same kind of object, so that the setAttrs function can send them back to the character sheet and update the values. You cant see the interior workings of the setAttrs function, because roll20 handles that side of it, but you can see it is a function just like the others ones described here - int, getAttrs, and the sheet worker itself. I know I';ve explained a lot more than you asked for, but it sounds like your are getting to the point where this will make sense to you - if not now, before too long - so it seemed worth explaining. Hopefully i did at least explain what you asked about, haha.
1587016490

Edited 1587016621
Yes, thank you = ).  I got all of that.  I still have some practice to do to memorize that shorthand java-script syntax, but if I refer back to this post it should all make sense.