I searched everywhere a simple example for loop a event whiteout the processor up to 70%. I don't find the awsner so i need help. I just need to everytime the page refresh it do the code.
Here the code:
page.once('load', async () => {
console.log("Page loaded!")
// Example of code to execute when the page reload
const searchBtn = await login.$x("//button[#id='btnEnter']");
await searchBtn[0].click();
});
You need code which executes everytime the page was loaded. You can achieve that by calling
function onLoadHandler() {
/**code goes here ***/
}
page.on('load', onLoadHandler)
You do not have to loop over this. Your onLoadHandler function will be executed everytime this particular page is reloaded.
What you did is to call page.once('load', onLoadHandler). This listens to the 'load' event once and then the handler removes it selve, it does it "once".
Loop
So probably if you looped the function page.once()(doing it 10000 times a sec) using a while loop or something your processor would ofc reach 70% usage .. Whenever your processor goes crazy like this, your code is suspected to be way to ressource heavy. In general that should not happen when running normal app without handling 10k requests per second or something.
Feel free to leave a comment.
Related
I need to create a webpage that will generate a bad First Input Delay (FID) value.
In case you aren't aware, FID is part of Google's Web Core Vitals.
I want to simulate a bad FID because I am testing a website scanning tool that is supposed to flag a bad FID value. Therefore I want to simulate a bad value on a webpage to make sure it works.
To be clear - I am NOT trying to fix my First Input Delay. I want to create a webpage that gives a bad First Input Delay value on purpose.
But I'm not sure how to do that.
I have a HTML page with <button id="button">Click Me</button>. And in the <head> I have added this script:
<script type="module">
// Get the First Input Delay (FID) Score
import {onFID} from 'https://unpkg.com/web-vitals#3/dist/web-vitals.attribution.js?module';
// Get the button element
const button = document.getElementById('button');
// Add a click event listener to the button
button.addEventListener('click', async () => {
// Make a delay
await new Promise((resolve) => setTimeout(resolve, 5000));
// Print the FID score to the console
onFID(console.log);
});
</script>
The imported onFID method is what Google uses from Web Vitals to report the FID value.
You can see a live version of the above script here: http://seosins.com/extra-pages/first-input-delay/
But when I click the button, 5000 milliseconds later it only prints a FID of about 3 milliseconds.
The 5000 millisecond delay is not included in the FID value.
Why doesn't it report the FID value as 5003 milliseconds?
When I try to simulate a bad FID value I am doing something wrong.
What could it be?
Update:
As suggested in the comments, I have also tried adding a delay on the server using a Cloudflare Worker. That worker delayed the server response by 5000 milliseconds. But it didn't work, because the FID value was unchanged.
Also I do not think this is the correct approach because FID measures the time from when a user first interacts with your site (i.e. when they click a link, tap on a button, etc) to the time when the browser is actually able to respond to that interaction. While the Cloudflare Worker was only slowing down the initial server response. Therefore I have since removed this experiment from the page.
I think you misunderstand what FID is
From web.dev's page on First Input Delay (FID):
What is FID?
FID measures the time from when a user first interacts with a page (that is, when they click a link, tap on a button, or use a custom, JavaScript-powered control) to the time when the browser is actually able to begin processing event handlers in response to that interaction.
and
💡 Gotchas
FID only measures the "delay" in event processing. It does not measure the event processing time itself nor the time it takes the browser to update the UI after running event handlers.
also:
In general, input delay (a.k.a. input latency) happens because the browser's main thread is busy doing something else, so it can't (yet) respond to the user. One common reason this might happen is the browser is busy parsing and executing a large JavaScript file loaded by your app.
Here is my understanding: Actual FID measurement is built into Chrome. The web-vitals library simulates this using browser measurement APIs. The measurement isn't based on when onFID is called; onFID simply sets up a measurement event listener with those APIs. What is measured is the time between when a user clicks on something (e.g. the button) and when its event handler is triggered, not how long that handler takes to complete (see second quote above).
First, we need something that occupies (i.e. blocks) the JS Event Loop
setTimeout does not do that. It just delays when something happens. In the meantime the event loop is free to do other work, e.g. process user input. Instead you need code that does exactly what you're not supposed to do: Do some lengthy blocking CPU-bound work synchronously, thus preventing the event loop from handling other events including user input.
Here is a function that will block a thread for a given amount of time:
function blockThread (millis) {
let start = Date.now()
let x = 928342343234
while ((Date.now() - start) < millis) {
x = Math.sqrt(x) + 1
x = x * x
}
}
or maybe just:
function blockThread (millis) {
let start = Date.now()
while ((Date.now() - start) < millis) {
}
}
Now the question is: When/where do we block the event loop?
Before I reached the understanding above, my first thought was to just modify your approach: block the event loop in the button click event listener. Take your code, remove the async, and call blockThread instead of setting a timer. This runnable demo does that:
// this version of blockThread returns some info useful for logging
function blockThread (millis) {
let start = Date.now()
let i = 0
let x = 928342343234
while (true) {
i++
x = Math.sqrt(x) + 1
x = x * x
let elapsed = (Date.now() - start)
if (elapsed > millis) {
return {elapsed: elapsed, loopIterations: i}
}
}
}
const button = document.getElementById('button');
button.addEventListener('click', () => {
const r = blockThread(5000)
console.log(`${r.elapsed} millis elapsed after ${r.loopIterations} loop iterations`)
console.log('calling onFID')
window.onFID(console.log)
console.log('done')
})
<!DOCTYPE html>
<html>
<script type="module">
import {onFID} from 'https://unpkg.com/web-vitals#3/dist/web-vitals.attribution.js?module'
window.onFID = onFID
</script>
<head>
<title>Title of the document</title>
</head>
<body>
<button id='button'>Block for 5000 millis then get the First Input Delay (FID) Score</button> 👈🏾 click me!
</body>
<p> 🚩 Notice how the UI (i.e. StackOverflow) will be unresponsive to your clicks for 5 seconds after you press the above button. If you click on some other button or link on this page, it will only respond after the 5 seconds have elapsed</p>
</html>
I'd give the above a try to confirm, but I'd expect it to NOT impact the FID measurement because:
Both your version and mine blocks during the execution of the event handler, but does NOT delay its start.
What is measured is the time between when a user clicks on something (e.g. the button) and when its event handler is triggered.
I'm sure we need to block the event loop before the user clicks on an input, and for long enough that it remains blocked during and after that click. How long the event loop remains blocked after the click will be what the FID measures.
I'm also pretty sure we need to import and call onFID before we block the event loop.
The web-vitals library simulates Chrome's internal measurement. It needs to initialize and attach itself to the browser's measurement APIs as a callback in order for it to be able to measure anything. That's what calling onFID does.
So let's try a few options...
start blocking the event loop while the page is loaded
Looking at the Basic usage instructions for web-vitals I arrived at this:
<!-- This will run synchronously during page load -->
<script>
import {onFID} from 'web-vitals.js'
// Setup measurement of FID and log it as soon as it is
// measured, e.g. after the user clicks the button.
onFID(console.log)
// !!! insert the blockThread function declaration here !!!
// Block the event loop long enough so that you can click
// on the button before the event loop is unblocked. We are
// simulating page resources continuing to load that delays
// the time an input's event handler is ever called.
blockThread(5000)
</script>
But I suspect that calling blockThread as above will actually also block the page/DOM from loading so you won't even have a button to click until it's too late. In that case:
start blocking after the page is loaded and before the DOMContentLoaded event is triggered 👈🏾 (my bet is on this one)
<script>
import {onFID} from 'web-vitals.js'
onFID(console.log)
</script>
<script defer>
// !!! insert the blockThread function declaration here !!!
blockThread(5000)
</script>
If that still doesn't work, try this:
start blocking when the DOMContentLoaded event is triggered
<script>
import {onFID} from 'web-vitals.js'
onFID(console.log)
// !!! insert the blockThread function declaration here !!!
window.addEventListener('DOMContentLoaded', (event) => {
blockThread(5000)
});
</script>
🌶 Check out this this excellent answer to How to make JavaScript execute after page load? for more variations.
Let me know if none of these work, or which one does. I don't have the time right now to test this myself.
I saw this code: https://gist.github.com/sidneys/ee7a6b80315148ad1fb6847e72a22313
This is pretty nice, a way to detect changes inside a page. I have the following code in addition to the above one:
(async () => {
const elems = await Promise.all([
'elem1',
'elem2',
'elem3',
].map(e => onElementReady(e, true)));
console.log(elems);
})();
The reason why I need to check for multiple elements is that there's a chaotic order of elements loading up via AJAX and one element isn't enough because it might be too early when the page is not fully loaded.
But this code that was provided by #wOxxOm doesn't work well when I need to call it every time a new page has loaded through AJAX. It works the first time when run, afterwards calling again the function that includes the above-mentioned code will no longer work.
Can you please help me?
I have a simple JavaScript function that manipulates the DOM (heights of elements for layout reasons).
The function get's called on window's resize (throttled to 1s) and on button click.
In my function everything is wrapped inside a _.delay() function in order for the script to wait 1s for a triggered animation to finish.
The problem is that sometimes the function get's called fast on after another and the second call starts before the first call ending. Now the function calls are doing their things simultaneously and everything get's bad.
My question:
How can I tell the function to only run one at a time? Some kind of lock would be good that locks the second call from executing. It would be great if this second call still executes, but only after the first call remove the lock.
Is something like this possible in JavaScript and how?
EDIT
Here is a code example of how the script looks like:
function doStuff() {
var stuff = $('[data-do-stuff]');
var height = stuff.height();
// Add CSS class that changes height of stuff
// Class starts an animation of duration of 1s
stuff.addClass('active-stuff');
// Wait 1s for the animation started by added class
_.delay(function() {
stuff.height(height * 42);
}, 1000);
}
$(window).on('resize', _.throttle(function() {
doStuff();
}, 1000));
$('.tasty-button').on('click', function() {
doStuff();
});
This is not a working example, just a gist of what the general structure of my script is.
If I e.g. click multiple times on the tasty button (about 3x in 1s) it messes with everything. (In my real script, I have got more trigger so just disabling the button for 1 second doesn't do the trick -.-)
I would like it to behave like this: If doStuff executes, lock every call until doStuff finishes with executing and then execute the locked calls afterwards.
PROMISES in Javascript is what you are looking for.
Without code examples, it's hard to suggest solutions specific to your question. However, here's some thoughts on your overall problem:
What you're experiencing is a called a "race condition" where a part of your application depends on multiple functions finishing at undetermined times.
Generally, there are two ways to handle situations like this:
1) Use callbacks. About Callbacks
2) As another user suggested, use JS promises. About JS Promises
This is a very simple use case. Show an element (a loader), run some heavy calculations that eat up the thread and hide the loader when done. I am unable to get the loader to actually show up prior to starting the long running process. It ends up showing and hiding after the long running process. Is adding css classes an async process?
See my jsbin here:
http://jsbin.com/voreximapewo/12/edit?html,css,js,output
To explain what a few others have pointed out: This is due to how the browser queues the things that it needs to do (i.e. run JS, respond to UI events, update/repaint how the page looks etc.). When a JS function runs, it prevents all those other things from happening until the function returns.
Take for example:
function work() {
var arr = [];
for (var i = 0; i < 10000; i++) {
arr.push(i);
arr.join(',');
}
document.getElementsByTagName('div')[0].innerHTML = "done";
}
document.getElementsByTagName('button')[0].onclick = function() {
document.getElementsByTagName('div')[0].innerHTML = "thinking...";
work();
};
(http://jsfiddle.net/7bpzuLmp/)
Clicking the button here will change the innerHTML of the div, and then call work, which should take a second or two. And although the div's innerHTML has changed, the browser doesn't have chance to update how the actual page looks until the event handler has returned, which means waiting for work to finish. But by that time, the div's innerHTML has changed again, so that when the browser does get chance to repaint the page, it simply displays 'done' without displaying 'thinking...' at all.
We can, however, do this:
document.getElementsByTagName('button')[0].onclick = function() {
document.getElementsByTagName('div')[0].innerHTML = "thinking...";
setTimeout(work, 1);
};
(http://jsfiddle.net/7bpzuLmp/1/)
setTimeout works by putting a call to a given function at the back of the browser's queue after the given time has elapsed. The fact that it's placed at the back of the queue means that it'll be called after the browser has repainted the page (since the previous HTML changing statement would've queued up a repaint before setTimeout added work to the queue), and therefore the browser has had chance to display 'thinking...' before starting the time consuming work.
So, basically, use setTimeout.
let the current frame render and start the process after setTimeout(1).
alternatively you could query a property and force a repaint like this: element.clientWidth.
More as a what is possible answer you can make your calculations on a new thread using HTML5 Web Workers
This will not only make your loading icon appear but also keep it loading.
More info about web workers : http://www.html5rocks.com/en/tutorials/workers/basics/
I want to display a spinner before some complicated function, i.e. dummyCounter(). The code looks like:
function add1() {
msg.html('start counting~<br \>');
document.body.appendChild(div);
spinner.spin(div);
// display spinner before doing stuff
dummyCounter();
}
jsfiddle: http://jsfiddle.net/eGB5t/
However the spinner shows after the dummyCounter() function is finished.
I try to use callback to force spinner display earlier but still no good. Can anybody help? Thanks.
jsfiddle: http://jsfiddle.net/eGB5t/2/
You have a thinking failure. Spinners are usually used for asynchronous tasks, so you can see that there is something in progress. A callback is then used to remove the spin when the async action has finished, since you cannot tell before it starts when it will finish.
I made up a quick example to show you, how such an async function would work in this case, and you can clearly see how the spinner appears slightly before "google finished" appears.
http://jsfiddle.net/eGB5t/4/
I added the following instead of your counting method:
$.ajax("http://google.de").always(function() {
msg.append("google finished");
});
You add the spin before you count, then it counts, then you could remove the spinner. This is perfecty fine. Thing is, if you would count to let's say 9999999999999 (so it would take some seconds), a normal for loop like you're doing is completely blocking the browser, so you won't have any repaints (and therefore no spinner) at all, while the loop is running.
What you would have to do (in this case) is to introduce a worker to have multithreading functionality in javascript.
var x;
function add1() {
msg.html('start counting~<br \>');
spinner.spin(div);
x= setTimeout(document.body.appendChild(div),500);
}