Async Template loading - javascript

I'm trying to asynchronously load 25 html templates
Here's my code:
var loopingLoadTemplate = function(index){
var name = names[index];
$.get('templates/' + name + '.html', function (data) {
that.templates[name] = data;
tplCount++;
console.log(tplCount + " " + names.length);
if (tplCount === names.length){
callback();
}
});
};
for (i = 0; i < names.length; i++){
loopingLoadTemplate(i);
}
tplCount is a counter that I keep so I know when it's safe to fire the callback
the problem is that, there are 25 templates to load, and when I checked under network tab in Chrome console, I see all 25 templates getting loaded properly, but console.log tells me the tplCount stops at 21, which I have no idea why. Is it because the for loop is firing so fast that some callbacks of the $ functions did not fire?
How do I safely asynchronously load all these templates?
So I also tried an synchronously fallback using recursive calls, but it mysteriously stops after loading some templates and gives no warning sign
var loadTemplate = function (index) {
var name = names[index];
console.log("loading " + names[index]);
$.get('templates/' + name + '.html', function (data) {
that.templates[name] = data;
index++;
if (index < names.length) {
loadTemplate(index);
console.log("trying another load");
} else {
callback();
console.log("trying callback");
}
});
};
loadTemplate(0);

OK, just figured out why its failing
so, indeed all the templates are loaded properly, but, if a html template has no content, the data in the callback function becomes undefined, instead of being empty as what I had expected
when the data is undefined, it fails the line:
that.templates[name] = data;
without any warnings or errors and the rest of the code in the callback are not excuted
since all the templates do get loaded, checking the network tab would give all status success result

Related

phantomjs not rendering webpage to png [duplicate]

This question already has an answer here:
PNG is not being rendered using PhantomJS with multiple attempts in a loop
(1 answer)
Closed 6 years ago.
well my code is something like this just a few lines
var a = "http://lnmtl.com/chapter/renegade-immortal-chapter-";
var b = 558;
var d = "rennegrade_ch";
var f = ".png";
var page = require('webpage').create();
var i = 0;
for (i = b; i < 560; i++) {
var c = a + i;
console.log(c);
page.open(c, function () {
var e = d + i + f;
console.log(e);
page.render(e);
});
}
phantom.exit();
the webpage can be rendered individually but once i put it inside for loop all it does is print the first console output properly but the second one it skips i guess its not entering the page.open function then for loop value increases then same thing happens again I have no idea why its not entering render function i tried to put var page = require('webpage').create();
inside for loop too but still no change
UPDATE: On another question stackoverflow.com/questions/31621577/png-is-not-being-rendered-using-phantomjs-with-multiple-attempts-in-a-loop?rq=1
it was pointed that this method wont work because of async nature of function but the example code provided in it isnt helpful enough can anyone example and i also tried set timeout as suggested in it still same thing happens so any other idea ?
Your phantom.exit() call kills the PhantomJS browser before you do any rendering. You have to wait for the rendering to end before you can exit(). You need to have some mechanism to say when the rendering is done. I'd suggest wrapping each of your renders in a Promise. Then using a Promise.all() to wait for all the render promises to resolve. After they resolve, exit PhantomJS.
Right now, you have what is below, which does not respect the asynchronous nature of page.open():
for (...) {
// I probably wont finish because phantom dies almost immediately
page.open(c, function () {
// I won't finish running since phantom dies
page.render(e);
});
}
// I'm going to kill the phantom almost immediately
phantom.exit();
You want something like the code below, which will wait for all the renders to finish. This will put renders of each of the sites we provide in a subdirectory "renders".
Note: You will need to install the es6-promise shim for this to work since PhantomJS does not yet support Promises. Thanks for the comment about that Artjon B
/*jslint node:true*/
/*globals phantom, sayHello*/
"use strict";
var Promise = require("es6-Promise").Promise;
// Array of URLs that we want to render
var urlArr = [
{
name: "google",
url: "http://www.google.com"
},
{
name: "yahoo",
url: "http://www.yahoo.com"
},
{
name: "bing",
url: "http://www.bing.com"
}
];
// Map URLs to promises
var proms = urlArr.map(function (url) {
// Return a promise for each URL
return new Promise(function (resolve, reject) {
// Make a page
var page = require("webpage").create();
// Open the URL
console.log("opening: " + url.name);
page.open(url.url, function () {
// Render the page
page.render("render/" + url.name + ".png");
console.log("done rendering: " + url.name);
// Say that we are done with rendering
resolve();
});
});
});
// Wait for all rendering to finish
Promise.all(proms).then(function () {
console.log("closing phantom");
// Exit phantom
phantom.exit();
});
For async request inside loop you should use asynchronous library so you can debug your code and don't get memory leak issue
async-js will be good in your case
npm install async
var async = require('async');
var a = "http://lnmtl.com/chapter/renegade-immortal-chapter-";
var b = 558;
var d = "rennegrade_ch";
var f = ".png";
var page = require('webpage').create();
var i = 0;
async.whilst(
function() {
return i < 560;
},
function(callback) {
i++;
var c = a + i;
console.log(c);
page.open(c, function() {
var e = d + i + f;
console.log(e);
page.render(e);
callback(null, i);
});
},
function(err, n) {
if(err) console.log(err);
phantom.exit();
});

"Outsource" recurring code into a function with parameters (js)

so i use one js file to load multiple html and js files whenever they are needed. I have a working code for plenty modules. In the example below you can see the first two modules. All of them look exactly the same. Now i want to "outsource" recurring code into a function with parameters so that the code-amount overall gets minimized. Since i have never done something like this before i could need some help (i am learning js at the moment). I would realy appreciate some help.
//first module
if (moduleID === "placeone") {
var isLoaded = 0;
if (isLoaded) {
console.log("file already loaded");
returnValue = new PlaceOneModule(id, moduleInitialData);
}
$("#placeone").load("html/modules/PlaceOneModule.html", function (response, status, xhr) {
console.log("PlaceOneModule.html" + " " + status);
$.getScript("js/modules/PlaceOneModule.js").done(function () {
console.log("PlaceOneModule.js geladen");
isLoaded = 1;
returnValue = new PlaceOneModule(id, moduleInitialData);
}).fail(function () {
console.log("PlaceOneModule.js nicht geladen");
});
});
}
//second module
if (moduleID === "placetwo") {
var isLoaded = 0;
if (isLoaded) {
console.log("file already loaded");
returnValue = new PlaceTwoModule(id, moduleInitialData);
}
$("#placetwo").load("html/modules/PlaceTwoModule.html", function (response, status, xhr) {
console.log("PlaceTwoModule.html" + " " + status);
$.getScript("js/modules/PlaceTwoModule.js").done(function () {
console.log("PlaceTwoModule.js geladen");
isLoaded = 1;
returnValue = new PlaceTwoModule(id, moduleInitialData);
}).fail(function () {
console.log("PlaceTwoModule.js nicht geladen");
});
});
}
The question is rather complex to answer, as there are many things to account for.
var cache = {};
function module(name, target, done) {
if (!(name in cache)) {
return $(target).load('html/modules/' + name + '.html', function(response, status, xhr) {
console.log(name + '.html ' + status);
$.getScript('js/modules/' + name + '.js')
.done(function() {
console.log(name + '.js geladen');
cache[name] = window[name];
done(null, cache[name]);
})
.fail(function() {
var message = name + '.js nicht geladen';
cache[name] = function() {
console.error(message);
};
done(message);
});
});
}
setTimeout(function() {
done(null, cache[name]);
}, 0);
}
I'll try to explain my train of thought behind this:
var cache = {} - you will need something to keep track of each individual module
function module(name, target, done) {
name would be the base name of your module, e.g. PlaceTwoModule, this was already used consistently across the html and js files and the js function name
target would be the selector where the html file should be loaded
as one of the actions you take requires async operation, the entire functionality needs to become async, I introduce a callback (done) argument
if (!(name in cache)) - if the module is not yet cached, it requires some fetching, so the load is triggered first thing
once the load completes, it will fire the $.getScript
if the $.getScript works out, the name will be assumed to be in window and a reference is stored in the cache variable, after that, the done callback is invoked (with the function as second argument).
if the $.getScript didn't work out, we add a function to the cache, which does nothing more than telling you it will not work, after that, the done callback is invoked (with an error as first argument).
if the name did exist in the cache, we will be calling the done callback right after we exit the module function
So, how to use this?
It now boils down to calling the module function
module('#placeone', 'PlaceOneModule', function(error, PlaceModule) {
if (error) throw new Error(error);
var instance = new PlaceModule(id, initial);
// ...
}
I have used the common function(error, value) {..} signature for the callback function, which always has the error as first argument (allowing for other arguments to be added and made optional).
There are some caveats/assumptions worth mentioning:
I have not provided a failsafe for preventing multiple loads of the same module (so it is the same as in your example) if earlier calls to module are still loading
no matter what target you invoke module with, it will only load 'once' (well, see the previous line ;-) )
I assume the loaded modules are in the global (window) scope in order to keep the example simple, keep in mind to not 'pollute the global scope'
This has become a rather elaborate answer, I hope I explained every step involved sufficiently.
Something like this possibly:
var modules = [];
modules.push({
js: 'PlaceOneModule',
id: 'placeone'
});
modules.push({
js: 'PlaceTwoModule',
id: 'placetwo'
});
var module = modules.filter(function(m) {
return m.id === moduleID;
});
if (module) {
var isLoaded = 0;
if (!!window[module.js]) {
console.log("file already loaded");
returnValue = window[module.js];
}
$("#" + module.id).load("html/modules/" + module.js + ".html", function(response, status, xhr) {
$.getScript("js/modules/" + module.js + ".js").done(function() {
returnValue = new window[module.js](id, moduleInitialData);
}).fail(function() {
console.log("PlaceOneModule.js nicht geladen");
});
});
}

Promise resolve misses one value for asynchronous request loop

Thanks to the help from here I could build a loop for posts and resolve the promises in order to handle asynchronous requests. But for some reason the loop to get the resolved promises and according values always misses one step. I tried to apply JotaBe's answer and it worked out, except there is one value missing. Everything else is fine.
The simplified function I call is:
var logs = function (outString, saveCSV) {
var promises = [];
var count = 0;
for (i = 1; i <= maxDevice; i++) {
promises.push($.post(Type1));
promises.push($.post(Type2));
promises.push($.post(Type3));
}
promises.push($.post(Type4));
var promiseResolve = $.when.apply($, promises);
promiseResolve.then(function () {
console.log(promises[1].promise().state());
console.log(promises[2].promise().state());
for (i = 0; i < promises.length; i++) {
promises[i].then(function (data, textStatus) {
var src = this.url;
var arg = arguments;
console.log(i + ": " + textStatus);
if (posttype2 or 3){String1 += data
} else if (posttype4) > 0)
{
String2 += data
} else
{
string3 += data
}
});
}
outString += String3+ "\n" + String2+"\n" + string1;
saveCSV(outString, filename);
});
};
The console.log(i + ": " + textStatus);shows
0: success
2: success
3: success
4: success
5: success
6: success
7: success
8: success
9: success
So i = 1is never resolved, even though console.log(promises[1].promise().state()); states promises[1] IS resolved.
If I set a breakpoint before the promiseResolve the missing promise is handled, though, while delaying the code with a timeout doesn't seem to help.
I also tried to use .done, instead of .then with the same result. The data missing is the largest data package of the loop. It can't be the syntax, since the other promises fetched with the same get in the loop resolve just fine.
So what am I doing wrong, as I can't understand why this one is missing. As far as I understood $when.applyis used to make sure the promises are resolved (which the console log states are).
How to handle this to get ALL values into the outstring?
Edit:
I tried some more console.log lines to check for values, or if something isn't resolved. So a console.log(i); right before the promises[i].then(function (data, textStatus)´ shows i = 1 is called. The row is complete 0, 1, 2... but theconsole.log(i + ": " + textStatus);after the function misses the 1, so it shows ´promises[1].then(function (data, textStatus) {...} is not called.
Logging
console.log(promises[1].promise().state());
console.log(promises[1].responseText);
right before the for.. loop shows the promise state is "resolved" and the responseText shows the string I want to attach to the outstrings. I also tried the proposed solution of Jaromanda X, but it did not help (thanks for the time), neither did using different combinations of using .doneinstead of .thenin either resolve function.
Putting a breakpoint before the promiseResolve.then seems to help, even if I click "run" as fast as I can. Have to try shortening that time, to be sure.
Adding another set of things I tried:
Splitting the posts/gets and the resolve into two functions and using the resolve as callback brought no success, neither did changing i to some unused variable, nor using for promise in promises to initiate the loop. I start running out of ideas to try, even more as promiseResolve returns resolved right at the promiseResolve.then function, so the request in promise[1] should be finished.
What seems to work, though I do not understand why and doesn't feel like the right way to solve the problem is encapsulating everything inside the promiseResolve.then function into a window.setTimeout(function(){...},1, so it looks more like
window.setTimeout(function(){
for (i=0 ; i < promises.length; i++) {
...
};
outString += spotString + "\n" + ioString + "\n" + logString;
saveCSV(outString, filename);
}, 1);
So, this one ms delay helps, but it doesn't feel like a clean solution. Can anyone explain why, or what I am missing? There must be a better way.
you have asynchronous code within the for loop that is using i
try this code
promiseResolve.then(function () {
console.log(promises[1].promise().state());
console.log(promises[2].promise().state());
for (i = 0; i < promises.length; i++) {
(function(i) { // ***** add this
promises[i].then(function (data, textStatus) {
var src = this.url;
var arg = arguments;
console.log(i + ": " + textStatus);
if (posttype2 or 3) {
String1 += data
} else if (posttype4) > 0) {
String2 += data
} else {
string3 += data
}
});
}(i)); // ***** add this
}
outString += String3 + "\n" + String2 + "\n" + string1;
saveCSV(outString, filename);
});
You'll have to make the changes from the invalid javascript to your actual code as above ... the additional lines are marked
what that code does is create a closure where i wont be changed before it is logged due to the asynchronous nature of the function it is logged in

child process spawn returning NaN

I'm trying to understand child process module in node.js, I created a parent file which has code:
var spawn=require("child_process").spawn;
var child=spawn("node",['plus_one.js']);
setInterval(function(){
//var number=Math.floor(Math.random()*10000);
var number=10;
child.stdin.write(number + "\n");
child.stdout.once("data",function(data){
console.log("Child replied to "+number + " with " + data);
})
},1000);
child.stderr.on("data",function(data){
//process.stdout.write(data);
console.log("error"+data)
})
The child file looks like this:
process.stdin.resume();
process.stdin.on("data",function(data){
var number;
try{
number = parseInt(data.toString(), 10);
number+=1;
process.stdout.write(number+"\n");
}
catch(err){
process.stderr.write(err.message+"lol");
}
})
If I execute just the child file it works fine , but when i execute the main file it always return NaN; why is that?
Also as im trying to understand it, I quite do not understand the difference between child_process.spawn and .exec, spawn return stream so it has stdin/stdout while exec returns buffer, does it mean that .exec cannot communicate with child file (and vice versa) other than passing variable with options/env object in it?
Making a few tweaks to your code, it is now working for me:
var spawn = require("child_process").spawn;
var child = spawn("node", ['plus_one.js']);
var number = 10;
setInterval(function () {
child.stdin.write(number++ + "\n");
child.stdout.once("data", function (data) {
console.log("Child replied to " + number + " with " + data);
});
}, 1000);
child.stderr.on("data", function (data) {
//process.stdout.write(data);
console.log("error" + data)
});
Changes:
Used proper bracing on the setInterval() function.
Used .once() in the appropriate place so event handlers don't pile up.
Moved the number variable outside of the setInterval() scope so that it can retain its value from one call to the next.

Asynchronous loading data issue AngularJS and Firebase

I am having difficulties loading data from Firebase into my AngularJS factory and thus controllers.
Basically I have the following factory:
.factory('usersList', ['fbutil', function(fbutil) {
return {
all: function() {
return fbutil.syncArray('users');
},
get: function(userId) {
fbutil.syncArray('users').then( function(result) {
window.alert(result.length) // FIRST ALERT
var x = 0;
for (var i = 0, len = result.length; i < len; i++) {
if (result[i].uid == userId) {
window.alert("fount id") // SECOND ALERT
x = i;
} else {
window.alert("nope"); // THIRD ALERT
}
}
return result[x];
}) // then
} // get
} // return
}]); // usersList
And my controller looks like:
.controller('OverviewCtrl', ['$scope', 'fbutil', 'usersList', function($scope, fbutil, usersList) {
$scope.usersList = usersList.all();
$scope.testUser = usersList.get("simplelogin:29");
// OTHER CODE
};
}])
In my HTML file, when I call {{usersList}} then it produces the result:
[{"color":"#CC0066","email":"a#a.com","name":"Eva","uid":"simplelogin:27"},{"color":"#009933","email":"b#b.com","name":"Semko","uid":"simplelogin:28"},{"color":"#CC0066","email":"c#c.com","name":"Caroline","uid":"simplelogin:29"}]
But testUser does not load, just shows {{tesUser}} in the index file.
Does anyone know how to handle this correctly? Without using the then(), which in this example also does not work, I figured out from the first alert that the result.length equaled 0, which gave me the suggestion that I am dealing with asynchronous loading. That is why I am trying to handle it whit .then() but apparently it is not working.
To handle a promise in angularjs the best way it to use defer values, since it allows you to process it before returning the data while keeping everything non blocking. With $http I would process like this :
function get(id) {
var deferred = $q.defer();
var url = "anUrlwithid";
$http.get(url).success(function(data, status) {
logger.logDebug('Successful GET call to ' + url + '\n, results ' + status + ': ' + data);
deferred.resolve(function(data){
//do something to your data then return
});
}).error(function(data, status) {
logger.logDebug('Failed GET call to ' + url + '\n, results ' + status + ': ' + data);
deferred.reject(data);
});
return deferred.promise;
}
And to process it in the controller :
get(1).then(function(data){//update scope or do something with the data processed});
You should be able to use that with your fbutil since it returns a promise I think.
Hope it helps
More details on the Q module here : https://docs.angularjs.org/api/ng/service/$q
PS: the logger is one of my personal service, just use console.log instead

Categories

Resources