I am trying to implement the module YSlow into my own project. A function I want to use looks like this:
YSLOW.registerRuleset = function (ruleset) {
YSLOW.controller.addRuleset(ruleset);
};
From what I can tell, YSlow is assigned here in the code at the beginning:
if (typeof YSLOW === 'undefined') {
YSLOW = {};
}
What I want to be able to do is export this class and be able to use this function. I would usually just put:
exports.sayHello = function() {
return "Hello"
};
However, I am unsure how to properly communicate with registerRuleset() when YSLOW is in the beginning. If I put sayHello() in yslow.js I can use it fine, but for any function with YSLOW at the beginning it does not work. I have tried putting exports before and after YSLOW in a function but have had no success.
So my question is, how can I get around this? And for that matter, what is YSLOW.function() even doing exactly and what is the importance of it?
Thanks!
I'm not familiar with YSLOW, hence I'm not clear with your query.
If you are trying to check whether YSLOW class objects are loaded, below code may help to identify whether loaded or not loaded.
this code will keep polling for the objects to be ready and available. This will poll for 20 milliseconds (you can increase to more).
Its a Kind of hack, you can keep polling for the objects whether it is loaded and ready to use.
function checkLoaded() {
if (YSLOW && YSLOW.controller && YSLOW.controller.addRuleset) {
return true;
}
return false;
}
/* Start: polling to check if YSLOW Objects are ready */
var timeout = Date.now() + 10000,
startTime = Date.now();
var intervalID = window.setInterval(function () {
if (checkLoaded()) {
console.log("GOT Ready at: ", Date.now() - startTime + "ms");
window.clearInterval(intervalID);
initLoad();//do your stuffs after loading here.
} else if (Date.now() > timeout) {
console.log("Not loaded, kill the polling.");
window.clearInterval(intervalID);
failedLoad(); //do your stuff for not loaded here
}
}, 20);
Related
When events are queued with setTimeout/setInterval, and the user is viewing a separate tab, Chrome and Firefox enforce a minimum 1000ms lag before the event is executed. This article details the behaviour.
This has been discussed on StackOverflow previously, but the questions and answers only applied to animations. Obviously, an animation can just be forced to update to the latest state when a user re-enters the tab.
But the solution does not work for sequenced audio. I have Web Audio API playing several audio files in sequence, and setTimeout is used to countdown to when the next audio file plays. If you put the tab in the background, you get an annoying 1 second gap between each pattern -- an extreme flaw in an API designed for advanced audio.
You can witness this behaviour in various HTML5 sequencers, e.g. with PatternSketch -- just by entering a pattern, playing, and going to another tab.
So I'm in need of a workaround: a way to queue events without the 1000ms clamp. Does anyone know of a way?
The only solution I can think of is to have window.postMessage run every single millisecond and check each time if the event is to execute. That is definitely detrimental to performance. Is this the only option?
Apparently there is no event system planned for Web Audio API, so that is out of question.
EDIT: Another answer is to use WebWorkers per https://stackoverflow.com/a/12522580/1481489 - this answer is a little specific, so here's something more generic:
interval.js
var intervalId = null;
onmessage = function(event) {
if ( event.data.start ) {
intervalId = setInterval(function(){
postMessage('interval.start');
},event.data.ms||0);
}
if ( event.data.stop && intervalId !== null ) {
clearInterval(intervalId);
}
};
and your main program:
var stuff = { // your custom class or object or whatever...
first: Date.now(),
last: Date.now(),
callback: function callback() {
var cur = Date.now();
document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
this.last = cur;
}
};
var doWork = new Worker('interval.js');
doWork.onmessage = function(event) {
if ( event.data === 'interval.start' ) {
stuff.callback(); // queue your custom methods in here or whatever
}
};
doWork.postMessage({start:true,ms:250}); // tell the worker to start up with 250ms intervals
// doWork.postMessage({stop:true}); // or tell it just to stop.
Totally ugly, but you could open up a child popup window. However, all this does is transfer some of the caveats to the child window, i.e. if child window is minimized the 1000ms problem appears, but if it is simply out of focus, there isn't an issue. Then again, if it is closed, then it stops, but all the user has to do is click the start button again.
So, I suppose this doesn't really solve your problem... but here's a rough draft:
var mainIntervalMs = 250;
var stuff = { // your custom class or object or whatever...
first: Date.now(),
last: Date.now(),
callback: function callback(){
var cur = Date.now();
document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
this.last = cur;
}
};
function openerCallbackHandler() {
stuff.callback(); // queue your custom methods in here or whatever
}
function openerTick(childIntervalMs) { // this isn't actually used in this window, but makes it easier to embed the code in the child window
setInterval(function() {
window.opener.openerCallbackHandler();
},childIntervalMs);
}
// build the popup that will handle the interval
function buildIntervalWindow() {
var controlWindow = window.open('about:blank','controlWindow','width=10,height=10');
var script = controlWindow.document.createElement('script');
script.type = 'text/javascript';
script.textContent = '('+openerTick+')('+mainIntervalMs+');';
controlWindow.document.body.appendChild(script);
}
// write the start button to circumvent popup blockers
document.write('<input type="button" onclick="buildIntervalWindow();return false;" value="Start" />');
I'd recommend working out a better way to organize, write, etc. but at the least it should point you in the right direction. It should also work in a lot of diff browsers (in theory, only tested in chrome). I'll leave you to the rest.
Oh, and don't forget to build in auto-closing of the child window if the parent drops.
I planned to use setInterval to simply set a variable to false, which would be inspected by the main loop to stop. Example (note: this is an example only, the acutal code is not a while() loop which would be easy to reconstruct, but a quite complex, and long to execute script generated by a closed source software actually):
var running = true;
setInterval(function () {
if (running) {
console.log("Stopping now!");
running = false;
}
}, 100);
while (running) {
// do something ...
}
However it does not seem to work at least firefox drops a "busy script" box after a while. What's the problem with the code above? setInterval() may not be able to run if your script already runs otherwise? I couldn't find an exact specification what setInterval() does exactly.
I would need something like this, since I already have huge (and very long to execute) script, so I thought I will try to stop it after a while, then using setTimeout() to let the browser breath a bit and then continue: as the script itself does know its internal state so it can continue from any point, but it's not an option to modify the script actually ....
If it's not possible with setInterval, is there any alternative to this, without any modification in the "long to execute" code itself?
Thanks!
If it's not possible with setInterval, is there any alternative to this, without any modification in the "long to execute" code itself?
One possibility is to make that a web worker rather than trying to use it on the UI thread. Despite people repeatedly saying so, JavaScript is not single-threaded (JavaScript, the language, is silent on the subject), not even on browsers anymore. In the browser environment, there is one main UI thread, but you can spawn other worker threads (web workers). The worker(s) and the main UI code can communicate via postMessage / onmessage.
Here's an example of a web worker in action. This page uses JavaScript on the UI thread to start a web worker, which runs on a separate thread. The worker runs for 10 seconds, busily updating a counter (this is just to simulate a long-running, calculation-intensive process), and sends updates to the UI thread every second:
Main page:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Worker Example</title>
<style type="text/css">
body {
font-family: sans-serif;
}
</style>
<meta charset="UTF-8">
</head>
<body>
<script>
(function() {
var worker = new Worker("worker.js");
worker.onmessage = function(e) {
display("Worker says " + e.data);
};
display("Starting worker");
worker.postMessage("start");
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>
worker.js:
this.onmessage = function(e) {
var counter, lastUpdate, now;
if (e.data === "start") {
// Loop without yeilding for 10 seconds, sending updates
// to the UI every second.
start = lastUpdate = Date.now();
counter = 0;
do {
++counter;
now = Date.now();
if (now - lastUpdate > 1000) {
lastUpdate = now;
this.postMessage(counter);
}
}
while (now - start < 10000);
this.postMessage("Done");
}
};
(You're not required to make the worker wait for a message to start, but it's fairly common.)
The problem is that Javascript is single-threaded. Rewrite your while loop to use setInterval itself and everything should work, since you will release the thread at the end of each loop.
You should use setTimeout or setInterval instead while loop. JS runs in single thread, so infinite loop will freeze your browser.
var running = true;
setInterval(function(){
if(running){
console.log('Stopping now!');
running = false;
}
}, 100);
(function loop(){
// Do yours loop stuff
if( running ){
setTimeout(loop, 0);
}
})();
You should consider using Worker or writing asynchronous code.
Or you can modify your code.
var running = true;
var past = Date.now();
while (running) {
// do heavy calculations ...
if ((Date.now() - past) > 10) {
running = false;
}
}
Of course, blocking loops aren't good idea, but I don't see good way to satisfy requirement:
If it's not possible with setInterval, is there any alternative to this, without any modification in the "long to execute" code itself?
JavaScript runs in a single threaded event loop. What this means is while your code is running no other code can run. This is why your callback does not get executed.
You can workaround this by also making your while(running) be asynchronous. Consider doing the following:
var running = true;
var monitor = setInterval(function () {
if (running) {
console.log("Stopping now!");
running = false;
clearInterval(monitor);
}
}, 100);
var work = setInterval(function() {
if (running) {
// do something
} else {
clearInterval(work);
}
}, 1);
Don't forget to call clearInterval!
I have a js file that contains my closure, this file is loaded before jQuery, let's say it can't be moved. How can I pass in or check for jQuery with a view to use it in the closure?
This is what I've got so far:
(function MyClosure() {
var interval = setInterval(function() {
if (typeof jQuery !== 'undefined') {
doJqueryStuff();
clearInterval(interval);
}
}, 500);
function doJqueryStuff() {
// Some stuff with jQuery.
}
})();
It actually works, but is there a "better" way? I always think I'm doing something wrong whenever I use setInterval() for things like this, also the fact I am losing time in that 500ms.
You could wait and attach your execution to the window.onload event, assuming jQuery is loaded once the window is loaded...
window.onload = function() {
// do stuff with jQuery
};
Don't worry - while it does look hackish (at least to me and you) it isn't bad. Often times you need to wait until a complex object is initialized and you need to do the same thing. The best thing is to just ensure the order that your scripts load to solve any dependency issues - but as you requested let's assume the order can't be adjusted.
The only improvement I would suggest: adding an escape hatch to anonymous setInterval function. That way if jQuery never becomes available for some reason, the script can notify the user and stop checking.
var checkCount = 0;
var interval = setInterval(function() {
if (checkCount++ > 20) {
alert("jQuery could not be loaded - degrading user experience");
clearInterval(interval);
}
if (typeof jQuery !== 'undefined') {
doJqueryStuff();
clearInterval(interval);
}
}, 500);
Wait for the onload event on the script tag. In this case, the doJqueryStuff should be a global function.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" onload='doJqueryStuff()'></script>
I'm reloading a module this way:
require('./module.js'); // require the module
delete require.cache('/module.js'); // delete module from cache
require('/module.js'); // re-require the module
But there is a problem if the module contains something like this:
setInterval(function(){
console.log('hello!');
}, 1000);
Every time I reload the module a new setInterval is called, but the last one is NOT closed.
Is there any way to know about each module's (long) running functions so I can stop them before I require it again? Or any suggestions how can I make this work?
I'm open to any crazy ideas.
This is just a wild guess but you may be able to load the module within a domain.
When you are done use domain.dispose() to clear the timers:
The dispose method destroys a domain, and makes a best effort attempt
to clean up any and all IO that is associated with the domain. Streams
are aborted, ended, closed, and/or destroyed. Timers are cleared.
Explicitly bound callbacks are no longer called. Any error events that
are raised as a result of this are ignored.
I would simply set a reference to the interval and expose a method in order to stop it like:
var interval = setInterval(function () {
console.log('hello');
}, 1000);
var clearInt = clearInterval(interval);
I dont think you can hook into any events as you are simply deleting a reference. If it doesnt exist anymore it reloads. Before you do this call the clearInt function.
You could create an IntervalRegistry in your main application:
global.IntervalRegistry = {
intervals : {},
register : function(module, id) {
if (! this.intervals[module])
{
this.intervals[module] = [];
}
this.intervals[module].push(id);
},
clean : function(module) {
for (var i in this.intervals[module])
{
var id = this.intervals[module][i];
clearInterval(id);
}
delete this.intervals[module];
}
};
In your module, you would register the interval created there:
// module.js
IntervalRegistry.register(__filename, setInterval(function() {
console.log('hello!');
}, 1000));
When it's time to clean up, call this:
var modulename = '/full/path/to/module.js'; // !!! see below
IntervalRegistry.clean(modulename);
delete require.cache[modulename];
Remember that modules are stored with their full filename in require.cache.
I have an issue. One of my JS scripts needs Facebook SDK and Twitter widgets JS to load first. Facebook creates FB object, Twitter creates twttr object. Both of them create these objects AFTER my script fires, even though they're loaded from <head>.
I think solution is to periodically check if FB and twttr are defined, and then proceed with executing my script. But I have no idea how to do this.
I tried creating a loop
while (typeof FB === 'undefined' || typeof twttr === 'undefined' || typeof twttr.widgets === 'undefined') {
// run timeout for 100 ms with noop inside
}
But this clearly does not work as it keeps firing timeouts at a high speed and page hangs.
Please help me, I can't sleep because of this issue.
If the scripts are loaded in the normal, synchronous way, then just make sure that your <script> include appears after the library scripts in the document's <head>. If, on the other hand, those scripts are loading objects asynchronously (as seems to be the case), then create something like this:
function whenAvailable(name, callback) {
var interval = 10; // ms
window.setTimeout(function() {
if (window[name]) {
callback(window[name]);
} else {
whenAvailable(name, callback);
}
}, interval);
}
And use it like this:
whenAvailable("twttr", function(t) {
// do something
});
The function given in the second argument to whenAvailable will not execute until twttr is defined on the global window object. You can do the same thing for FB.
Important note: Those libraries probably also provide some built-in way to execute code after they have loaded. You should look for such hooks in their respective documentation.
Have you put your script to be executed on page load? (ie. body onload="do_this ();")
That should make your code execute once all external resources has been loaded.
Regarding the use of setTimeout
setTimeout will return immediately, if you'd like to wait for certain variable to be defined, use something as the below.
function when_external_loaded (callback) {
if (typeof FB === 'undefined' || typeof twtter === 'undefined') {
setTimeout (function () {
when_external_loaded (callback);
}, 100); // wait 100 ms
} else { callback (); }
}
...
when_external_loaded (function () {
alert (FB);
alert (twtter);
});
const checkIfLoaded = ('lib', cb) => {
const interval = setInterval(() => {
if (lib) {
typeof cb === 'function' && cb();
clearInterval(interval);
} else {
console.log('not yet');
}
}, 100);
}
If the Facebook scripts are being loaded asynchronously, Facebook has a supported way to execute code when it's library loads which should be much more efficient than polling for it. See this answer for an example: https://stackoverflow.com/a/5336483/816620.
If the Facebook scripts are being loaded synchronously, then you don't have to wait for them - they will load before any other scripts after them run.