JS - How Web Workers work? - javascript

I'm working on this code :
JS
<script src='file://C:\blablabla\JavaScript\bignumber.js-master\bignumber.js'></script>
<script>
document.write("<h1>\"blabla\"</h1>\n<h3>blabla</h3>");
function problem(){
var img = document.getElementById('problemi');
return img.style.display = img.style.display === 'block' ? 'none' : 'block';
}
function problem551(){
problem();
var t0 = performance.now();
var max = 1e+15;
var sum = new BigNumber(1);
for(var i=1;i<max;i++)
sum = sum.plus(scomponi(sum,0));
var t1 = performance.now();
document.getElementById("p551").innerHTML = 'blabla<span>'+max+"</span> blabla <span>" + sum +"</span> in <span>"+(t1 - t0)/1000+"</span> blaaa";
}
function scomponi(num,sum){
var str=num.toString();
for(var i = 0 ; i< str.length ;i++ ){
sum += parseInt(str[i]);
}
return sum;
}
</script>
HTML
<body>
<div>
<button onclick="problem551()" >PROBLEM 551</button>
<img id="problemi" src="PROBLEM551.png" style="display: none;">
<p id="p551"></p>
</div>
</body>
But Chrome crashes, it gives me this :
How can prevent this error on my function, he has a loop from 1 to 1e+15, so it takes too much time. I read something about WEB WORKERS but is unclared for me. I want to use it on my function problem551(), so someone can explain me how it works?

Move your functions to a new file, funcs.js:
And use new Worker("funcs.js") to start it.
You can use postMessage to send the result back as described in MDN:
Sending messages to and from a dedicated worker
The magic of workers happens via the postMessage() method and the
onmessage event handler. When you want to send a message to the
worker, you post messages to it like this (main.js):
first.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
second.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
So here we have two elements represented by the variables
first and second; when the value of either is changed,
myWorker.postMessage([first.value,second.value]) is used to send the
value inside both to the worker, as an array. You can send pretty much
anything you like in the message.
In the worker, we can respond when the message is received by writing
an event handler block like this (worker.js):
onmessage = function(e) {
console.log('Message received from main script');
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
console.log('Posting message back to main script');
postMessage(workerResult);
}
Do expect it to still take a lot of time, it is quite a long operation, but it will hopefully prevent Chrome from bringing up the error.
I see you are trying to solve Project Euler problem 551.
You are throwing away a valuable piece of information:
You are given a_10^6 = 31054319.
You don't need to begin your iterations from 1. The next number can any arbitary number in the sequence.
a_10^6 = 31054319
a_(10^6 + 1) = 31054319 + 3 + 1 + 0 + 5 + 4 + 3 + 1 + 9

Related

Having trouble slowing down fetch in javascript so as to avoid 429 errors

I wonder if someone could help me.
I have a non-professional development license for a reverse geocoder within a javascript program where I am only allowed to do two requests
per second otherwise I get a 429 error. I have 3 sets of co-ordinates I wish to feed into the reverse geocoder and I get the first two
processed correctly but after that I get an error and the third one isn't processed. I thought that if I used the SetTimeout function either in the for
loop or in one of the lower level functions this would delay the requests enough to be able to process all 3 addresses but no matter where I
place the SetTimeout function it continues to get the 429 error. When I log the time to the console, I can see that the three calls to the
reverse geocoder happen at the same time. Can anyone suggest where I can place the timeout to slow down the requests enough?
Thanks (last attempted version of code below)
for (let i = 0; i < mapMarkers.length; i++){
// use a reverse geocode function to build a display address for each of the co-ordinates chosen
SetTimeout(reverseGeocode(mapMarkers[i].getLatLng()), 1000);
};
function reverseGeocode(coords){
var today = new Date();
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
console.log("Into reverse geocoder " + time);
let REVERSEURL = `https:....`
let TOKEN = '.....'
let url = `${REVERSEURL}key=${TOKEN}&lat=${coords.lat}&lon=${coords.lng}`;
//do a reverse geocoding call
getData(url, coords);
}
async function getData(url,coords) {
try {
const data = await getRequest(url);
// create a display address with the first three elements containing something in the address dictionary
let address = createAddressString(data.address, 3) +
" ( " + coords.lat.toFixed(5) + ", " + coords.lng.toFixed(5) + " ) ";
// Insert a div containing the address just built
$("#addresses-container").append("<div class='request-page'>"+address+'</div>');
} catch(e) {
console.log(e);
}
}
async function getRequest(url) {
const res = await fetch(url);
if (res.ok) {
return res.json();
} else {
throw new Error("Bad response");
}
}
Your current logic is invoking the reverseGeocode() method immediately, as you pass the response from that function call to the timeout. You need to provide a function reference instead.
Even if you correct that issue, then you would instead delay all the requests by 1 second, but they would still get fired at the same time.
To stagger them you can use the index of the iteration to multiply the delay. For example, the following logic will fire 1 request every 250ms. This delay can be amended depending on what the rate limit is of your API provider. Also note that SetTimeout() needs to be setTimeout()
for (let i = 0; i < mapMarkers.length; i++) {
setTimeout(() => reverseGeocode(mapMarkers[i].getLatLng()), 250 * i);
}
Aside from the problem, it would be worth checking if the API can accept multiple lookups in a single request, which will alleviate the issue. Failing that, I'd suggest finding an alternative provider which allows more than 3 requests per N period.

Understanding how to use asynchronous functions in Javascript - counts and timers?

I'm trying to make a small page that runs a survey. I'd like the survey to be repeatable within the page, but the user needs to enter a login name when they enter the page. I'm attempting to write code such that they fill out the login, and then the survey begins. I'm trying to have a JSON that feeds in 100+ random images, sending in a new one each time the user completes the survey application.
So, whats happening is every time the start function is called, the survey function runs. However, it only runs for around 1 second, rather than the continuous running I'd like to have. When I've played with the ordering of the page and added timers, I've managed to get it such that the survey runs a single time but does not reset itself upon completion. It also doesn't seem to be picking up variables from the other function - I wanted the "count" variable to increase as the survey is repeatedly completed. It also isn't picking up the username variable, which is created inside of the outside "start" function.
I think my problem here has to do with not understanding how functions and variables are supposed to be nested inside of each other, and with how asynchronicity works in Javascript. Can anyone point me in the right direction? Here's a JSfiddle for the problem, and a code snippit:
//button Login
function serv (){
Survey
.StylesManager
.applyTheme("modern");
window.survey = new Survey.Model(json);
survey
.onComplete
.add(function (result) {
count ++;
var PID = username;
var results = PID + "|||" + (keyname) + ":\n" + JSON.stringify(result.data, null, 3) + (count) ;
document
.querySelector('#surveyResult')
.textContent = results;
survey.clear();
survey.render();
});
$("#surveyElement").Survey({model: survey});
}
function start() {
var username = document.getElementById("user_PID").value;
alert("your PID is " + username);
modal.style.display = "none";
serv();
}
var knlist = {}
var count = 13
var knx = ("kn" + count)
var keyname = (knlist[knx])
var mapilink = "https://images.mapillary.com/" + (keyname) + "/thumb-1024.jpg";
var json = {}
var modal = document.getElementById('id01');

writing a test "cookie clicker" game

In school we use this coding website called code.org. It's pretty handy and seems to be able to do anything that normal coding software can, just a bit more digestible for beginners such as myself. I'm asking a question that I'm not sure is even possible to answer. In the game I'm trying to figure out how to add cps (cookies per second) to the counter. My issue is that this could be done in a loop, but other things need to happen outside of the loop. So I'm not sure how to add them, but still be able to run other bits of code without it getting stuck in a loop. The code will be posted at the bottom. This project is just for fun and I do not intend to pass this work off as my own. Thanks for the help!
(please note that this IS the entirety of the code i have so far...)
var cookies = 0;
var incriment = 1;
var cps = 0;
var autoClickers = 0;
var autoClickerCost = 1;
var autoClickerAdd = 0.50;
var upgradeClickCost = 100;
setText("upgradeClickCostText","cost: "+ upgradeClickCost);
setText("autoClickerCostText", "cost: " + autoClickerCost);
onEvent("image1", "click", function() {
cookies = cookies + incriment;
console.log("you have: "+cookies+" cookies");
setText("cookieNumber", "Cookies: " + cookies);
});
onEvent("upgradeClick", "click", function() {
if(cookies >= upgradeClickCost){
cookies = cookies - upgradeClickCost;
console.log("you have: "+cookies+" cookies");
setText("cookieNumber", "Cookies: " + cookies);
incriment = incriment * 2;
upgradeClickCost = upgradeClickCost * 2;
setText("upgradeClickCostText", "cost: "+ upgradeClickCost);
}
});
onEvent("shopScrnBtn", "click", function() {
setScreen("shop_screen");
console.log("went to shop!");
});
onEvent("gameScrnBtn", "click", function() {
setScreen("game_screen");
console.log("went to cookie!");
});
function addCookies(){
cookies = cookies + cps;
}
onEvent("buyAutoClicker", "click", function() {
if(cookies >= autoClickerCost){
cookies = cookies - autoClickerCost;
autoClickers++;
console.log("you have: "+cookies+" cookies");
setText("cookieNumber", "Cookies: " + cookies);
autoClickerAdd = autoClickerAdd * autoClickers;
cps = cps + autoClickerAdd;
}
console.log("auto clicker purchased");
});
(also note that this code snippet does not work properly as you won't be on code.org or have the proper buttons to handle the events.)
The feature you are looking for is probably setInterval which runs a function every n milliseconds.
function runAutoClicker() {
cookies = cookies + cps;
}
// Run auto-clicker every second (every 1000 milliseconds)
setInterval(runAutoClicker, 1000);
I'm not seeing any loops here, just click events. Am I missing something? If there was a loop, we could see what's inside vs. what's out. Typically you handle variables changes (and not changing them) within loops with conditional if statements.

Web worker is a process of browser or thread of browsers' process? [duplicate]

I'm trying to understand this example:
HTML (main code):
<html>
<title>Test threads fibonacci</title>
<body>
<div id="result"></div>
<script language="javascript">
var worker = new Worker("fibonacci.js");
worker.onmessage = function(event) {
document.getElementById("result").textContent = event.data;
dump("Got: " + event.data + "\n");
};
worker.onerror = function(error) {
dump("Worker error: " + error.message + "\n");
throw error;
};
worker.postMessage("5");
</script>
</body>
</html>
Javascript (worker code):
var results = [];
function resultReceiver(event) {
results.push(parseInt(event.data));
if (results.length == 2) {
postMessage(results[0] + results[1]);
}
}
function errorReceiver(event) {
throw event.data;
}
onmessage = function(event) {
var n = parseInt(event.data);
if (n == 0 || n == 1) {
postMessage(n);
return;
}
for (var i = 1; i <= 2; i++) {
var worker = new Worker("fibonacci.js");
worker.onmessage = resultReceiver;
worker.onerror = errorReceiver;
worker.postMessage(n - i);
}
};
I have the following questions:
When exactly the worker code starts to run ? Immediately after the execution of var worker = new Worker("fibonacci.js"); ?
Is that true that onmessage = function(event) { ... } assignment in the worker code will be executed before worker.postMessage("5"); in the main code ?
Can worker code access global variables that are defined in the main code (like worker)?
Can main code access global variables that are defined in the worker code (like results)?
It seems to me that worker.onmessage = function(event) {...} in the main code has the same meaning like onmessage = function(event) {...} in the worker code (which is onmessage event handler of the worker). Where am I wrong ? What is the difference between them ?
What this code should actually do ? When I run it here it just prints "5". Is that what it is supposed to do, or I'm missing something ?
Thanks a lot !
Check out HTML5 Rocks: The Basics of Web Workers for general tutorial.
Workers will start as soon as you call the postMessage method of the worker.
the function bound to worker's onmessage in the main code will work when the worker calls postMessage.
global variables are not shared between main and worker threads. The only way to pass data is through messaging via postMessage.
as you suspected, the onmessage on both worker and main code has the same meaning. It is an event handler for when the thread receives a message event. You can even use addEventListener instead, catching message event:
Main Code:
function showResult(event) {
document.getElementById("result").textContent = event.data;
dump("Got: " + event.data + "\n");
}
var worker = new Worker("fibonacci.js");
worker.addEventListener('message', showResult, false);
Worker code:
addEventListener('message', resultReceiver, false);
The fibonacci example you took is a recursive worker example. If not using workers, it would be something like this:
function fibonacci(n) {
if (n == 0 || n == 1) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
var result = fibonacci(5);
dump("Got: " + result + "\n");
(oh no, I'm not going to do a stackless for you. You write it yourself!)
I also want to add that you can debug web workers only in Chromium based browsers. You have to select Sources in developer panel and in right column expand bottom line Workers and then choose check box pause on start.

How to slow down an Ajax call?

I have a function in JS contains a loop, that calls an AJAX call every iteration. The call to inserts checked elements into a DB and returns the results of those elements in the same page in the next section.
The problem I have is that when I check for e.g. 4 checkboxes out of 3 groupes, the only checkboxes of the last group gets added to the page. However, when I use alert(), I can see all elements.
I used setTimeout, but I got error in the code. I also added lines to give more time to AJX call, but the problem remains. So I wonder if there is a solution to slow down the code without using alert().
This is my script:
addAptitudeField : function(currentAutocompleteField, idChamp) {
var currentAutocompleteFieldBind = currentAutocompleteField;
var idChampBind = idChamp;
window.setTimeout(function() {
// Code ...
var paramDwr = {};
var newDivName = "div" + idChamp + lastValueId;
paramDwr[attributs.r_divId] = newDivName;
paramDwr[attributs.r_currentValue] = currentValue;
paramDwr[attributs.r_hiddenIdsField] = hiddenIdsField.id;
paramDwr[attributs.r_lastValueId] = lastValueId;
paramDwr[attributs.r_itemmod] = nbAptitudesCat % 2 == 0;
// setTimeout ( RepertoireDwr.ligneSuppEtSpanMessage, 1000 ) doesn't work
RepertoireDwr.ligneSuppEtSpanMessage(paramDwr, function(ajaxPage) {
divCategorie.update(divCategorie.innerHTML + ajaxPage.texte);
aptitudeAvecDetail.remetsValeursStockees();
var btnSuppression = $(newDivName).getElementsByTagName('img')[0];
btnSuppression.setAttribute("onclick", "formulaireFiche.updateCSS('" + newDivName + "');" + btnSuppression.getAttribute("onclick") + "fiche.updateCategorieSuppressionAptLieeUo(\'divCat" + currentCategorie + "\');"); });
}
//
// alert() : It works in this case.
//
// for (var i=0; i<5000000; i++) ; it doesn't work
}, 400);
}
Thank you in advance for your help and time.
I will likely be downvoted for mentioning this, because it is not a recommended procedure, but I believe every coder should have all facts.
In jQuery AJAX construct, there is option async:false, which will delay the script from continuing UNTIL the AJAX has completed processing. Needless to say, if things go wrong in the AJAX the browser could freeze. A lot depends on who your users are, and amount of traffic -- on a few of my ten-user in-house projects it was an acceptable solution.
$.ajax({
async: false,
type: 'post',
url: 'ajax/ax.php',
data: 'request=',
success: function(d){
if (d.length) alert(d);
}
});
Ref:
What does "async: false" do in jQuery.ajax()?
The better idea, however, is to look into the Promises interface, with methods like .when() and .then()
References:
https://jsfiddle.net/v86bc028/2/
http://jqfundamentals.com/chapter/ajax-deferreds#
http://digitizor.com/jquery-html-callback-function-using-promise/#
how does jquery's promise method really work?
The problem you're running into deals with asynchronous functions, or the A in AJAX. If you don't know what an asynchronous function is, there are many others who can explain it better than I can, so give that a google.
What's happening without the alert() in there is your code makes 4 sever calls, but all 4 get sent out before you get a response to any of them. With the alert() (or setTimeout), you're giving the code time to received each response to a call before the next one is made.
There are several ways you can approach this, the first way is by calling the next call after the first receives a response. The second way is to use an async function to call all 4 at once on different chains(?). I'm not the best at explaining this part, but there's plenty of code to be found on SO and online.
I think you have a more generic problem in your code, since you seem to need to delay your executions to wait till sth. else is finished, instead of getting anounced when it is done.
The line that annoys me most is this one
divCategorie.update(divCategorie.innerHTML + ajaxPage.texte);
what exactly is update doing? How is it implemented?
I assume it does sth. like divCategorie.innerHTML += ajaxPage.texte;
Wich is highly unfavorable, since the browser has to parse and rebuild, whatever there already is in divCategorie.innerHTML.
Just appending the new Markup would be better.
long way short: maybe a good hack would be to insert some hidden node as a placeholder (so you kan keep order, although the AJAX-requests may return in a different order) and replace that node with the real content, as soon as it arrives.
Kind of like this:
addAptitudeField : function(currentAutocompleteField, idChamp) {
var currentAutocompleteFieldBind = currentAutocompleteField;
var idChampBind = idChamp;
//this is done immediately, and therefore preserves the order of the loop,
//without any delays/timeouts
var placeholder = document.createElement("div");
placeholder.className = "placeholder";
placeholder.style.display = "none";
divCategorie.appendChild(placeholder);
window.setTimeout(function() {
// Code ...
var paramDwr = {};
var newDivName = "div" + idChamp + lastValueId;
paramDwr[attributs.r_divId] = newDivName;
paramDwr[attributs.r_currentValue] = currentValue;
paramDwr[attributs.r_hiddenIdsField] = hiddenIdsField.id;
paramDwr[attributs.r_lastValueId] = lastValueId;
paramDwr[attributs.r_itemmod] = nbAptitudesCat % 2 == 0;
// setTimeout ( RepertoireDwr.ligneSuppEtSpanMessage, 1000 ) doesn't work
RepertoireDwr.ligneSuppEtSpanMessage(paramDwr, function(ajaxPage) {
//convert the passed text into a DocumentFragment
var frag = fragment(ajaxPage.texte);
//replacing the placeholder with the fragment
divCategorie.insertBefore(frag, placeholder);
divCategorie.removeChild(placeholder);
aptitudeAvecDetail.remetsValeursStockees();
var btnSuppression = $(newDivName).getElementsByTagName('img')[0];
//this is also pretty horrible to me:
btnSuppression.setAttribute("onclick", "formulaireFiche.updateCSS('" + newDivName + "');" + btnSuppression.getAttribute("onclick") + "fiche.updateCategorieSuppressionAptLieeUo(\'divCat" + currentCategorie + "\');"); });
}
}, 400);
}
I think you should do some major refactoring. And take a look into Promises.
// * -> DocumentFragment
//strings/primitives are parsed as HTML-markup,
//null / undefined is ignored
//Arraylike structures are parsed recursively
var fragment = (function(container){
return function(src){
return reducer(document.createDocumentFragment(), src);
}
function reducer(frag, node){
var i, len, fc, c, r;
if(node === Object(node)){
if("nodeType" in node){
//dom nodes
frag.appendChild(node);
}else{
//Arraylike structures, like NodeLists or jQuery-Objects, or just plain Arrays
for(i = 0, len = ("length" in node && node.length)|0, r = reducer; i < len; (i in node) && r(frag, node[i]));
}
}else if(node != null) {
//strings (all primitives)
for((c=container).innerHTML = node; fc = c.firstChild; frag.appendChild(fc));
}
return frag;
}
})(document.createElement("div"));

Categories