I want to execute 2 functions in a specific that I have imported from 2 other .js files that I have made. The function that needs to complete first takes a bit of time and the 2nd one starts before the first is ended and I need files the first one created for it to work. Here's basically what my .js looks like:
var pdfToPng = require("./pdfToPng.js");
var doStuffToPng = require("./doStufftoPng.js");
var pdfFilePath = process.argv[2];
var pngFilePath = pdftoPng.convert(PdfFilePath);//convert takes a path
//and makes a png and returns path
//to the png
doStuffToPng.doStuff(pngFilePath);
//I want "doStuff()" to start AFTER "convert()" is done.
Im pretty sure it has something to do with callbacks, but I'm a javascript noob and need help. I can get it to work with setTimeout(), but that seems like a "duct tape fix" to me. Is there some way more elegant?
Edit: some wonderful people wanted to help and asked to post this, the pdfToPng.js:
var spindrift= require('spindrift');//this is a node module
var fs = require('fs');
//Makes a png from pdf in pngFolder and returns the path to that png
exports.convert = function(path)
{
var pdf = spindrift(path);
var pathToPng = path.substring(0, path.length-4); //takes off the .pdf
pathToPng += "_out.png";
//this is spindrift's stuff, makes a png in dir pngFolder/pathToPng
pdf.pngStream(500).pipe(fs.createWriteStream("pngFolder/" + pathToPng));
return "pngFolder/" + pathToPng;
}
Welcome to the async world of javascript. The function callback though created synchronously is executed asynchronously. So you have to modify the code to get doStuff executed only after you know for sure that convert function has executed. You can find how this can be done # Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
if so, you need to implement your own callback,
- Open pdftoPNG.js
- modify convert function with one more parameter
function convert(PdfFilePath, finishConvert) {
//now just insert this line where you finally instead of return
//remove return yourpngpath; //or something, i assume
//add following in the place of return
finishConvert(yourpngpath);
}
Then Please call like this
var pdfToPng = require("./pdfToPng.js");
var doStuffToPng = require("./doStufftoPng.js");
var pdfFilePath = process.argv[2];
pdftoPng.convert(PdfFilePath,function(path){
if(path!="") {
doStuffToPng.doStuff(path);
}
});
You have to update your convert method to support callbacks/ promises.
Here is an example using Callbacks
exports.convert = function(path, fnCallback)
{
var pdf = spindrift(path);
var pathToPng = path.substring(0, path.length-4); //takes off the .pdf
pathToPng += "_out.png";
//this is spindrift's stuff, makes a png in dir pngFolder/pathToPng
pdf.pngStream(500).pipe(fs.createWriteStream("pngFolder/" + pathToPng));
if (fnCallback && typeof(fnCallback) === "function") {
fnCallback("pngFolder/" + pathToPng);
}
}
You'll see the following
A new parameter being passed in fnCallback
A check to make sure that the parameter is passed in and that it is a function
The fnCallback function gets called with the results passed in as a parameter
Removing of the return statement
Now when the convert method is called, after the long running process completes, the Callback method will get executed.
To call the modified convert method you now have to pass in a callback function
function myCallback(path){
// do something with the path
}
pdftoPng.convert(PdfFilePath,myCallback);
Related
function getArray() {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
var my_array = [];
j.addEventListener('load',function(){
// Some jQuery codes to fill my_array
});
return my_array;
}
I use above code to dynamically load jQuery in console, and then use jQuery to get some data from the DOM and store them in the array. However, it returns an empty array. I think it is because loading jQuery takes some time and the function gets returned before the jQuery is loaded and the jQuery codes are executed.
So before getArray() returns, I must make sure the jQuery codes have been executed. I've tried to put return my_array inside the addEventListener, of course it won't work because that way it will return the anonymous function. I can think of some ways to deal with this issue, like making the my_array a global so I don't have to return the function, or putting the jQuery loading codes to another loadjQuery function and call it before I execute the jQuery codes, but is there a better way to do it?
The problem is due to asynchronous call of loading jquery script.
The best way to do it will be, write a function to load a script and pass the callback function, then on successful load of script call your callback function, eg:
function loadScript(callback) {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
j.addEventListener('load',function(){
if(typeof(callback) == "function")
});
}
function getArray(){
var my_array = [];
// Some jQuery codes to fill my_array
return my_array;
}
loadScript(getArray)
Unfortunately, that can't be done. JavaScript is an event based single-thread asynchronous language. What you're trying to do can't work in that type of environment.
However, it's likely you simply need to load jQuery before processing this function (even using a simple <script> tag) to solve your issue. Otherwise, you're likely to encounter a very noticeable delay when calling the function due to the downloading & evaluating of the jQuery library. Another issue would be that if you call the function more then 1 time, you'll load jQuery again, and that might create a big big mess.
Alternatively, if you "insist" on using jQuery & have it only loaded once your function is called, you could return a Promise that will resolve to your array like this (This will require a supporting browser or some polyfills / promise library):
function getArray() {
var j = document.createElement('script');
j.src = "http://code.jquery.com/jquery-2.1.4.min.js";
var head = document.getElementsByTagName('head')[0];
head.appendChild(j);
return new Promise(function(resolve, reject) {
j.addEventListener('load',function(){
var my_array = [];
// Some jQuery codes to fill my_array
resolve(my_array);
});
});
}
I'm using Node.js + Express + nodejs-sqlite3 to make a form that when submited will insert a new row on an slite3 database.
On query sucess I want to write certain response.
So the small big problem is just: Modify a string that will be storing the html to be shown, inside the callback function of sqlite3.run()
I read about closures, and passing an object with methods to modify its own attributes. But it seems it's not working. It will pass the object attributes and methods, but no change will remain when the callback function ends. I read that objects will be passed as reference, not copies.
This is the code:
app.post("/insert.html", function(req, res){
function TheBody(){
this.html = "";
this.msg = "";
this.num = "";
}
TheBody.prototype.add = function(string){
this.html = this.html + string;
}
var body = new TheBody();
body.msg = req.body.message;
body.num = req.body.number;
var insertCallback = function(data){
return function(err){
if( err != null){
console.log("Can't insert new msg: " + err.message);
data.add("ERROR-DB");
} else {
console.log("Ok. Inserted: " + data.msg);
console.log(data.html);
data.add("OK - MSG: "+data.msg+" NUM: "+data.num);
console.log(data.html);
}
};
};
var db = new lite.Database('database.db');
var query = "INSERT INTO outbox (message, number) VALUES (?, ?)";
db.run(query, [body.msg, body.num], insertCallback(body) );
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', body.html.length);
res.end(body.html);
}
On server side I'll see
Ok. Inserted: TestString
[Blank space since data.html still has no information]
OK - MSG: TestString NUM: TestNumber [Showing that indeed was modified inside the function]
But on the client side res.end(body.html); will send an empty string.
The object is not being passed as reference.
What's missing in the code, and what simpler alternatives I have to change a string variable inside a callback anonymous function?.
I already know I could use response.write() to write directly on the function if it were more simpler. But I discovered it would only work if I use response.end() inside the callback, otherwise (being outside as it is now) it will meet a race condition where the buffer will be closed before sqlite3.run() be able to use response.write().
-------- Answered --------
As hinted by Justin Bicknell and confirmed by George P. Nodejs-sqlite3 functions are run asynchronously. So I was ending the stream to the client before the callback would be called, thus nothing was being printed.
This was a problem more about "This is SPART- nodejs, so write your stuff according to events'" rather than a logic one. I found this kind of programming kind of convoluted but nobody else than me told me to use nodejs. For those wondering about how one could put some order over the order of queries on the database, nodejs-sqlite3 functions returns a database object that is used to chain the next query.
Since I was printing the information to the client just once in every handled event, the resulting object ended like this:
function TheBody(response){
this.response = response;
}
TheBody.prototype.printAll = function(string){
this.response.setHeader('Content-Type', 'text/html');
this.response.setHeader('Content-Length', string.length);
this.response.end(string);
}
Preferring that to clutter all the code a lot of res.setHeader() lines.
node-sqlite3 methods are, by default, run in parallel (asynchronously). That means that your code is going through this timeline:
Your code calls db.run(...)
Your code calls res.end(...)
db.run completes and calls your callback.
This is the source of a huge number of questions here on SO, so you can almost certainly find a better answer than anything that I could write here in a reasonable amount of time.
I would start here: How does Asynchronous Javascript Execution happen? and when not to use return statement?
There's a foreign JS file which is not loaded from our server on my page. In short I dont have any control on that file and I can't edit the file. I have created 1 function which I wish to call after the function which is in that foreign file is called.
I know the name of function of foreign file. I wish to call my function once the function from foreign file is executed. How can I do it?
Let me know if you dont understand my problem here. I'll try and explain it again.
Thanks in advance
If the foreign function is in the global scope you can replace it with your own wrapper:
var _old = foreignFunction;
foreignFunction = function() {
// put pre-call stuff here
...
// call with the original context and args
_old.apply(this, arguments);
// and post-call stuff here
...
}
Note the use of .apply to ensure that whatever context and arguments were supplied to the original function are still supplied to it. Without that, the this variable inside that function may not be what it's supposed to be.
You have to replace the foreign function with your own. This may or may not be possible, depending on the order in which things are executed.
After the function (ForeignFunc()) is loaded onto the page
var _originalForeignFunc = ForeignFunc;
ForeignFunc = function() {
_originalForeignFunc();
//now do whatever you want to do here
alert("After Foreign Func Execution");
}
Just remember, this will only work if you can replace ForeignFunc() with your own definition before it's actually called.
I use callbacks all the time when using 3rd part libraries such as jquery, however I've run into an issue where I need to set up my own call back. Take for instance my current code:
// Get All Rates
function getAllRates() {
$('#shiplist tr.mainRow').each(function() {
var curid = $(this).attr("id");
var cursplit = curid.split("_");
var shipid = cursplit[1];
getRate(shipid);
});
}
This iterates through all the table rows that have class "mainRow", and each main row has the id of "shipment_#####" so for each row it splits by the _ to get the actual ship id, which it then uses to calla "getRate" function which sends an ajax call to the server using UPS or USPS api's to get the rate for the given shipment.
This works fine for UPS all rows start loading at once and return their rates independently. The problem is now with Stamps.com, they use an authenticated soap conversation so an authenticator string needs to be received from the first call and used in the next call and so on.
So basically I need to modify the above function to execute the "getRate()" function and wait for it to complete before executing the next iteration.
Anyone know how I can do this with out a lot of fuss?
EDIT - Clearification on the question being asked:
I want to take the following function:
getRate(shipid);
and access it like so:
getRate(shipid, function(shipList) { _Function_data_here_ });
When I define the getRate function, how do I define it so that it has that callback ability? how does that work?
I would suggest you extract all the IDs at once and pass them to your function. That way, you can easily process the first on individually and the rest afterwards. With deferred objects, this is really easy.
For example:
function extractAllIds(callback) {
var ids = $('#shiplist tr.mainRow').map(function() {
return this.id.split('_')[1];
}).get();
callback(ids);
}
function getRates(ids) {
var first = ids.shift();
$.ajax({data: first, ...}).done(function(response) {
// extract the authenticator string
for(var i = ids.length; i--; ) {
$.ajax({data: ids[i], ...});
}
});
}
extractAllIds(getRates);
In any way, you cannot make $.each wait for response of Ajax calls.
How do I specify more arguments to be passed to a jsonp callback function?
For example, I'm trying to grab youtube video data:
http://gdata.youtube.com/feeds/api/videos/gzDS-Kfd5XQ?v=2&alt=json-in-script&callback=youtubeFeedCallback
The javascript callback function that will be called is youtubeFeedCallback and it contains only one argument when called.
As of now the function would be something like this,
function youtubFeedCallback(response) {
...
}
What I would like to be able to do is pass a second argument like this,
function youtubeFeedCallback(response, divId) {
...
}
Is this possible to do. I've tried looking everywhere online and couldn't find anything.
Thanks!
You can't add arguments to the callback function like that. However, you can generate a wrapper function. The JSONP callback function just was to be a function in the default namespace, that means that you just need to add a generated function with a known name to the global window object. Step one is to make up a name:
var callback_name = 'youtubeFeedCallback_' + Math.floor(Math.random() * 100000);
In the real world you'd want to wrap that in a loop and check that window[callback_name] isn't already taken; you could use window.hasOwnProperty(callback_name) to check. Once you have a name, you can build a function:
window[callback_name] = function(response) {
youtubeFeedCallback(response, divId);
};
You'd want to that up a little bit more though:
function jsonp_one_arg(real_callback, arg) {
// Looping and name collision avoidance is left as an exercise
// for the reader.
var callback_name = 'jsonp_callback_' + Math.floor(Math.random() * 100000);
window[callback_name] = function(response) {
real_callback(response, arg);
delete window[callback_name]; // Clean up after ourselves.
};
return callback_name;
}
Once you have something like that wired up, you could just call:
jsonp = jsonp_one_arg(youtubeFeedCallback, divId);
And then use the value of jsonp as the callback value in the YouTube URL.
You could build more functions like this to handle longer arguments lists too. Or you could build a general purpose one with arguments and apply.
better way is specify an associated array of divId and videoId like this
var arr = {
'gzDS-Kfd5XQ': 'divId_1',
'gwWS-Gasfdw': 'divId_2'
};
and in callback function get your divId by videoId
function youtubFeedCallback(data)
{
var divId = arr[data.entry.media$group.yt$videoid.$t];
}
When creating a "script" object, you can add attributes to it, and reference them later with document.currentScript. This worked for me but other people may need something more elegant.
function blah(stuff,extraarg){
cs=document.currentScript;
console.log(stuff); // stuff is your json output
console.log(cs.extraarg); // cs.extraarg is your extra arg
}
function thing(extraarg){
var url='https://....&json_callback=blah';
var s=document.createElement('script');
s.src=url;
s.extraarg=extraarg;
document.body.appendChild(s);
}