I'm trying to have my backbone application check the server as often as possible for updates to a model, similar to how twitter's site has new tweets that are automatically added.
My current setup is checking an external application through their api so I have no access to their server which leaves me to rely on the client side to do the checking without being too memory hungry, how can I achieve this?
In Javascript the only way you can really control timing is through setTimeout/setInterval; there is no "more sophisticated" mechanism, unless you count helper functions (eg. 'delay') which just wrap setTimeout/setInterval.
So, dmi3y's answer was correct. However, since you mentioned Backbone in both the tags and in the description, here's a more Backbone-ish version...
var YourModelClass = Backbone.Model.extend({url: remoteUrl});
var instance = new YourModelClass();
var seconds = 5;
window.setInterval(_.bind(instance.fetch, instance), 1000 * seconds);
or, if you wanted to build it in to your class ...
var YourModelClass = Backbone.Model.extend({
url: remoteUrl,
initialize: function() {
var seconds = 5;
window.setInterval(_.bind(this.fetch, this), 1000 * seconds);
}
});
var instance = new YourModelClass();
It's also worth mentioning that setInterval returns an object which you can pass to clearInterval if you want to stop "polling".
P.S. Just in case you're not familiar with _.bind, it comes from the Underscore library, which Backbone depends on so you already have it. All it does is fix this in place, so that when your timeout/interval function resolves, the this inside it will be the second argument to _.bind (and not window, which is what it would normally be).
possible solution
(function IcallTheShoots(){
console.log('am I?'); // any way you able communicate with server
window.setTimeout(IcallTheShoots, 1500);
})();
why setTimeout instead of setInterval, cause it makes sure next cycle will be called only when current is finished
Related
I wanted to use rxjs for the first time but am a bit stucked 'cause it doesn't behave exactly like I want it to: In my scenario I want to create an observable from a promise. But I want the promise only being called once (not on every subscription) and I want it not being called on creation time (defer the call to the first subscription).
First I tried this:
var source = Rx.Observable.fromPromise(_this.getMyPromise())
which causes a call to the getMyPromise function right on creation time. This is not satisfying because at that time I don't know if the source really will be used.
Then I tried:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() })
which causes a call to the getMyPromise function each time a new subscription is being made to source. This makes way too many unnecessary calls to the web server. The Rx.Observable.create function seems to have the same issue.
So what is left or what am I missing?
.shareReplay() does this, e.g.:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() }).shareReplay();
If you're using rxjs5, you'll want to read: Pattern for shareReplay(1) in RxJS5
In answer to your comment below, I can think of a fairly straightforward extension to the above logic that will do what you want, but it has a caveat. Let's say the events you want to use to trigger a "refresh" are represented in a stream, s$, then you could do something like:
var source = Rx.Observable.of({}).concat(s$)
.flatMapLatest(function() {
return Rx.Observable.defer(function() {
return _this.getMyPromise()
})
})
.shareReplay(1)
What we have here is a stream starting with a dummy object to get things rolling, followed by a stream consisting of your refresh events. Each of these is projected into a new observable created from a fresh invocation of your getMyPromise method, and the whole thing is flattened into a single stream. Finally, we keep the shareReplay logic so we only actually make calls when we should.
The caveat is that this will only work properly if there's always at least one subscriber to the source (the first subscription after all others are disposed will run the promise again, and will receive both the previously-cached value and the result of the promise it caused to run).
Here is an answer that does not require at least one subscriber at the source at all times using a simple helper:
var _p = null;
var once = function() { return _p || (_p = _this.getMyPromise());
var source = Rx.Observable.defer(once);
Or if you're using lodash, you can _.memoize your getMyPromise and get this automatically.
I'm prerendering my HTML pages for the search engines bots via PhantomJS through Selenium, so that they can see the fully loaded content. Currently, after PhantomJS reached the page, I'm waiting 5 seconds so that I'm sure everything is loaded.
Instead of waiting those 5 seconds every time, one solution I contemplate is to wait until an attribute html-ready on the <body /> tag is set to true:
<html ng-app>
<head>...</head>
<body html-ready="{{htmlReady}}">
...
</body>
</html>
.controller("AnyController", function($scope, $rootScope, AnyService) {
$rootScope.htmlReady = false;
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$rootScope.htmlReady = true;
});
})
The question is: will the html-ready attribute always be set to true after any view update has been done (e.g. displaying the anyData)? In other words, is it possible that during a laps, the html-ready attribute is true while the page is not fully loaded yet? If yes, how can it be handled?
It should be done after the digest, thus it has more chances to work as expected.
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$timeout(function () {
$rootScope.htmlReady = true;
}, 0, false);
});
But it is useless in terms of the app. You have to watch for changes in every single place, Angular doesn't offer anything to make the task easier.
Fortunately, you are free to abstract from Angular and keep it simple.
var ignoredElements = [];
ignoredElements = ignoredElements.concat($('.continuously-updating-widget').toArray());
var delay = 200; // add to taste
var timeout;
var ready = function () {
$('body').off('DOMSubtreeModified');
clearTimeout(timeoutLimit);
alert('ready');
};
$('body').on('DOMSubtreeModified', function (e) {
if (ignoredElements.indexOf(e.target) < 0) {
clearTimeout(timeout);
timeout = setTimeout(ready, delay);
}
});
var timeoutLimit = setTimeout(ready, 5000);
Feel free to angularify it if needed, though it isn't the production code anyway.
It is a good idea to put the handler into throttle wrapper function (the event will spam all the way). If you use remote requests on the page that can potentially exceed timeout delay, it may be better to combine this approach with several promises from async services and resolve them with $q.all. Still, much better than looking after every single directive and service.
DOMSubtreeModified is considered to be obsolete (it never was really acknowledged, MutationObserver is recommended instead), but current versions of FF and Chrome support it, and it should be ok for Selenium.
Short answer
No. It isn't guaranteed that your markup will be completely rendered when html-ready is set.
Long answer
To the best of my knowledge it's not possible to accurately determine when Angular has finished updating the DOM after the model changed. In general it happens very fast and it doesn't take more than a few cycles to finish, but that's not always the case.
Correctly detecting when a page has finished loading/rendering is actually quite a challenge, and if you take a look at the source code of specialized tools, like prerender, you'll see that they use several different checks in order to try to decide whether a page is ready or not. And even so it doesn't work 100% of the time (Phantom may crash, a request may take longer than usual to complete, and so on).
If you really want to come up with your own solution for this problem, I suggest that you take a look at prerender's source code (or another similar project) to get some inspiration.
I have a function that performs a long task. I would like to create a function that is able to notify the caller of the progress. Ultimately I want to update the UI with the current progress.
Something like this:
function myLongMethod(progressCallback)
{
for(var i = 0 ... )
{
progressCallback(i) ;
}
}
This works but updates on UI are not smooth. Is there a better way? I would prefer something with a jquery Deferred object using deferred.notify(). Any ideas?
Your code is fine. You have got another problem. Javscript always runs on the UI thread. Your operation is blocking this thread (the browser) and you will see some blocking of your browser window.
Luckily there is a workaround implemented in modern browser called web workers. It's simple just call in your main script another script which then get executed:
var w = new Worker("another_script.js");
If your worker is ready you can react on the result by adding a event listner to the worker:
w.onmessage = function(event) {
//do something
}
When you use this pattern, your UI did not block. You can even return data from a web worker and include scripts into it. More details you can find here and here is a good starting tutorial.
Hi you can apply the easing effect to your UI for smoothness and i am giving the following code it may help you
var oldProgress = 0;
var uiUpdater = null;
function updateUI(newProgress){
if(uiUpdater !=null){
// update your ui to the old progress first
window.clearInterval(uiUpdater); // clearing the previous timer
}
var diff = newProgress - oldProgress;
oldProgress = newProgress;
var stepSize = diff/5; // applying the new change in 5 steps to the UI
uiUpdater = window.setInterVal(function(){
// updating your UI after every 100 milliseconds
// to give the smoothness
diff -= stepSize; // decreasing the difference gradually
if(diff<=0){
window.clearInterval(uiUpdater); // clearing the interval once the update is done
}
},100);
}
You have to call the "updateUI" method from you callback with the new progress.
I am developing a web application in node.js to collect data from devices on a network using snmp. This is my first real encounter with node.js and javascript. In the app each device will be manipulated through a module I named SnmpMonitor.js. This module will maintain basic device data as well as the snmp and database connection.
One of the features of the app is the ability to constantly monitor data from smart metering devices. To do this I created the following code to start and stop the monitoring of the device. It uses setInterval to constantly send a snmp get request to the device. Then the event listener picks it up and will add the collected data to a database. Right now the listener just prints to show it was successful.
var dataOIDs = ["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"];
var intervalDuration = 500;
var monitorIntervalID;
var dataCollectionEvent = "dataCollectionComplete";
var emitter = events.EventEmitter(); // Uses native Event Module
//...
function startMonitor(){
if(monitorIntervalID !== undefined){
console.log("Device monitor has already started");
} else {
monitorIntervalID = setInterval(getSnmp,intervalDuration,dataOIDs,dataCollectionEvent);
emitter.on(dataCollectionEvent,dataCallback);
}
}
function dataCallback(recievedData){
// receivedData is returned from getSnmp completion event
// TODO put data in database
console.log("Event happened");
}
function stopMonitor(){
if(monitorIntervalID !== undefined){
clearInterval(monitorIntervalID);
emitter.removeListener(dataCollectionEvent,dataCallback);
} else {
console.log("Must start collecting data before it can be stopped");
}
}
//...
I also have a test file, test.js, that requires the module, starts monitoring, waits 10 seconds, then stops it.
var test = require("./SnmpMonitor");
test.startMonitor();
setTimeout(test.stopMonitor,10000);
My problem is that the setInterval function in startMonitor() is not being run. I have tried placing console.log("test"); before, inside, and after it to test it. The inside test output never executes. The monitorIntervalID variable is also returned as undefined. I have tested setInterval(function(){ console.log("test"); },500); in my test.js file and it runs fine with no issues. I feel like this is a noobie mistake but I just can't seem to figure out why it won't execute.
Here is a link to the entire module: SnmpMonitor.js
I not sure exactly what was wrong but I got it to work by overhauling the whole class/module. I thought the way I had it was going to allow me to create new monitors objects but I was wrong. Instead I created two functions inside the monitor file that do the same thing. I changed the start function to the following.
SnmpMonitor.prototype.start = function() {
var snmpSession = new SNMP(this.deviceInfo.ipaddress,this.emitter);
var oids = this.deviceInfo.oids;
var emit = this.emitter;
var duration = this.intervalDuration;
this.intervalID = setInterval(function(){
snmpSession.get(dataCollectionEvent,emit,oids);
},duration);
};
The setInterval function seems to work best when the callback function is set inside an anonymous function, even though technically you can pass it directly. Using the this. notation I created some class/module/function variables (whatever its called in js) that are in scope of the whole class. For some reason the variables accessed through this. do not work so well when directly in a function or expression so I created temp variables for them. In my other version all the variables were global and js doesn't seem to like that.
I've been messing with Chrome Heap profiler and some basic Backbone objects just to get an idea of garbage collection. I ran into some curious and confusing behavior.
Using a base page that has only jquery-1.7.2,underscore-1.3.3, and backbone-0.9.2 loaded.
If I run the following code from a Chrome (win/19) js console window the collection will not be garbage collected. In the heap profiler, 'Object's retaining tree' it will list the 50 users and the circular references between the models and the collection, but it will also list 2 that say b in function() #234234. Expanding out those references the references look to be from jQuery's ajax callback handlers.
(function () {
var collection = new Backbone.Collection();
collection.url = '/api/users/';
collection.fetch();
setTimeout(function wtf() { $('#content').append($('<div>' + collection.models[0].get('name') + '</div>')); }, 3000);
})()
If I run the following code the collection will be properly garbage collected as expected. The difference is the last line where I make some random ajax call that seems to shift the jQuery ajax callbacks off the collection.
(function () {
var collection = new Backbone.Collection();
collection.url = '/api/users/';
collection.fetch();
setTimeout(function wtf() { $('#content').append($('<div>' + collection.models[0].get('name') + '</div>')); }, 3000);
$.ajax({ url: '/api/users/' });
})()
Is this expected behavior or a bug or what? It'd suck if I have random objects hanging around because they had done ajax requests in the past.
Update: so I was testing this further and it appears to only affect the first or second instances. If I create a setinterval that creates a new collection and calls fetch every 1 second the first 1-2 instances created will remain in memory indefinitely (20min+) while all other instances created will be disposed properly.
If I call jquerys Ajax function and in the success handler hand the data to the new collection as a parameter at creation this behavior does not occur at all. All instances created are cleaned up properly.
It appears that the function Backbone.wrapError is part of the problem along with the wrapping of success.